结构体的类型

结构体分为:1. named-field(具名结构体?),2. tuple-like(类元组结构体?),3. unit-like(类单元?)。
Rust的命名习惯:类型使用CamelCase(或叫PascalCase),方法和字段使用snake_case。
想要使用结构体表达式创建结构体必须对所有的字段可见。这也是为什么String和Vec这种本身是结构体的类型不能直接创建,必须使用类型关联方法创建。

创建结构体时可以用另外一个相同类型的结构体来声明省略的字段。

  1. struct S { a: i32, b: i32, c: i32 };
  2. let s1 = S { a: 1, b: 2, c: 3};
  3. let s2 = S {a: 4, ..s1};

tuple-like structpub struct Bounds(pub usize, pub usize);基本上就是个tuple。这类结构体适合用来构造新类型,struct Ascii(Vec<u8>);比单纯的Vec<u8>更能说明这个类型是用来做ASCII字符串的。

Unit-Like Structs 定义方式:struct Onesuch; 是一个不占用内存的类型,Rust从类型可以推断其需要的操作,不用申请内存。省略头尾的Range .. 其实就是一个Unit-Like Struct RangeFull

各种结构体的内存结构都一样,按照某种方式。Rust并不保证字段的顺序。结构体中按字段类型本身是否需要堆内存来确定是否使用指针和堆内存。可以用属性#[repr(C)]让编译器按C的方式处理结构体的内存。

方法

其实应该叫 associate function(关联函数?)

实例方法的self参数除了可以是实例本身和引用以外,还可以是smart pointer(智能指针?聪明指针?)。这样的方法也只能被相应类型的实例所调用。

  1. impl Queue {
  2. pub fn sp(self: Box<Self>) {
  3. println!("{:?}", self.older);
  4. }
  5. }
  6. // 这个方法可以被 let mut bq = Box::new(Queue::new()); 调用bq.sp();
  7. // 不能被let q1 = Queue::new(); 调用

但定义为smart pointer的实例可以调用普通类型的方法。
smart pointer类型的方法在调用的时候也会被转移,借用和可变借用。
方法里的聪明指针的一个优点在于让程序员选择内存的用法,

  1. struct Node {
  2. tag: String,
  3. children: Vec<Rc<Node>>
  4. }
  5. impl Node { // 普通类型self
  6. fn append_to(self, parent: &mut Node) {
  7. parent.children.push(Rc::new(self));
  8. }
  9. }
  10. impl Node { // smart pointer self
  11. fn append_to(self: Rc<Self>, parent: &mut Node) {
  12. parent.children.push(self);
  13. }
  14. }

如果使用上面的方法,每次append_to都需要重新去堆上申请内存将self转移进去,并创建Rc指向这个内存。但如果使用smart pointer版本就可以根据场景选择内存的使用:

  • 如果调用者本身是一个node: Rc<Node>且能够转移所有权,直接node.append_to(parent),不用申请内存也不用增加引用计数。
  • 如果调用者想要保留所有权,只需要node.clone().append_to(parent),只增加了指针技术,不用复制内存。
  • 如果调用者本身是node: Node才需要使用复制内存的办法:Rc::new(owned).append_to(&mut parent)

不把方法和类型定义放一起,而是放到单独的impl块中的好处:

  • 方便查找所有的data成用
  • 给所有的类型(例如:另外两种结构体,enum,基础类型)提供了相同的实现方法的方式。
  • 以及类似的方式实现trait

关联常量

可以在impl块中定义常量,这些常量一般用来:

  • 定义一些这个类型常用的常量,比如如果定义了一个二维向量可以定义常量来表示原点和单位向量 ```rust pub struct Vector2 { x: f32, y: f32, }

impl Vector2 { const ZERO: Vector2 = Vector2 { x: 0.0, y: 0.0 }; const UNIT: Vector2 = Vector2 { x: 1.0, y: 0.0 }; }

  1. - 可以用来定义名字和id,用来区别类似的类型。
  2. ```rust
  3. impl Vector2 {
  4. const NAME: &'static str = "Vector2";
  5. const ID: u32 = 18;
  6. }

泛型结构体

泛型结构体的impl<T> Queue<T>,是给任何类型T定义关联函数。impl后面的T看着有的啰嗦,实际是为了区分impl Queue<String>。后者定义的关联函数只关联到Stirng类型的Queue上。
特殊类型Self代表了Queue<T>
调用某个实际类型的关联函数时用turbo fish语法:let mut q = Queue::<char>::new();

内部可变性(interior mutability)

一般的类型,如果定义成mut才能修改类型本身或内部成员的值。Rc是永远不能可变,std::cell模块提供一些可变性的功能。

std::cell::Cell

let cell = Cell::new(value)可以通过set方法永远可变。
cell.get:获取内部值的复制,只有内部值有Copy trait才能操作。
cell.set:修改内部的值,会将原值丢弃,替换成新值。
Cell适合用来处理基础类型,比如计数器。

std::cell::RefCell

let ref_cell = RefCell::new(value)也是将value转移进去。
ref_cell.borrow()返回一个不变指针Ref<T>指向内部的值,如果已有可变引用会panic。
ref_cell.borrow_mut()返回一个可变指针RefMut<T>指向内部的值,如果已有引用会panic。
ref_cell.try_borrow(), ref_cell.try_borrow_mut()类似前两个方法,返回一个Result而不是panic。