不是每个trait都可以作为tarit对象被使用,这和类型大小是否确认有关。每个trait都包含一个隐式的类型参数Self,代表实现该tarit的实际类型。Self默认有一个隐式的tarit限定?Sized
,形如 Self:?Sized
。?Sized trait包含了所有的动态大小类型金额所有的可确定大小的类型。Rust中大多数类型都是可确定的,就是<T:Sized>。
必须满足下面2条规则才可以当作trait对象使用:
- trait的Self不能被限定为Sized。
- trait中所有的方法都必须是对象安全的。
而对象安全的方法必须满足一下三点之一:
- 方法受Self:Sized约束
- 方法签名同时满足以下三点:
(1)必须不包含任何泛型参数。
(2)第一个参数必须为Self类型或可以解引用为Self的类型
(3)Self不能出现在第一个参数以外的地方,包括返回值 - trait中不能包含关联常量。
在实践中,只涉及到两条规则。如果一个 trait 中所有的方法有如下属性时,则该 trait 是对象安全的:
返回值类型不为 Self
方法没有任何泛型类型参数
每个文件定义一个模块。lib.rs定义了一个和自己crate同名的模块;一个mod.rs定义了一个它所在文件夹名字的模块;其他的每个文件定义了一个同文件名的模块。
二进制crate的root必须是main.rs,库crate的root必须是lib.rs
单元测试通常和所测试的代码在同一个文件
集成测试,样例,benchmarks 都必须像其他用户一样导入crate,只能用公开的API。
闭包
- 如果闭包中没有捕获任何环境变量,则默认自动实现Fn
- 如果闭包中捕获了复制语义类型的环境变量,则
- 如果不需要修改环境变量,无论是否使用了move,均会自动实现Fn
- 如果需要修改环境变量,则自动实现FnMut
- 如果闭包中捕获了移动语义类型的环境变量,则
- 如果不需要修改,且没有使用move,则自动实现FnOnce
- 如果不需要修改,且使用了move,则自动实现Fn
- 如果需要修改,则自动实现FnMut
- 使用move,如果捕获的变量是复制语义类型的,则闭包本身会自动实现Copy/Clone,否则不会。
每个闭包表达式都是一个独立的类型,这会有一些不便,如不能把不同的闭包保存到一个数组中,但这可以通过把闭包当做trait对象来解决。把闭包放到Box<T>中就可以构建一个闭包的trait对象,然后就可以当做类型来使用,
三者关系:
FnOnce-> FnMut -> Fn,即要实现Fn,必须先实现前面2个。
rust类型分为复制语义和移动语义,复制语义是指分配在栈上,所以复制的时候很简单,直接按位复制,不会出现内存不安全的情况。移动语义指分配在堆上,为了保证内存安全,才有了所有权系统,即一块内存只有有一个变量指向它。
对于复合类型来说,是复制还是移动,取决于其成员的类型,分为2种:
- 结构体,枚举体:
当成员全都是复制语义的时候,复合类型不会自动实现Copy,要手动实现Derive(Copy,Clone),此时复合类型才是复制语义的。如果复合类型中的成员有移动语义的,则无法实现Copy。 - 元组,数组,Option:类型会自动实现Copy,如果元素均为复制语义,则元组就是复制,不需要手动再Derive(Coyp,Clone),否则元组就是移动语义的。
共享可变状态是万恶之源
每个let都会创建一个默认的词法作用域,这个作用域就是它的生命周期(lifetime),就是在这个词法作用域中存活,出了就死亡。
解引用会获得所有权。
显式生命周期参数是为了解决跨函数借用,编译器无法检查的问题。它只用于编译器的借用检查,来防止垂悬指针。
'b: 'a
的意思是b的存活时间长于a
结构体实例的生命周期应短于或等于任意一个成员的生命周期。
生命周期省略规则:
- 每个输入上对应一个不同的生命周期参数
- 如果只有一个输入,则输出生命周期等于这个输入的生命周期
- 如果有self(&self,&mut self),则输出生命周期等于self 的生命周期
trait对象的生命周期默认以下规则:
- trait对象的生命周期默认是'static
- 如果实现trait的类型包括&'a x或&'a mut x,则默认生命周期就是'a
- 如果实现trait的类型包含多个类似T:'a的从句,则生命周期需要明确指定
Cell和RefCell的区别:
- Cell<T>通过set/get来直接操作包裹的值,RefCell<T>通过borrow/borrow_mut。
- Cell<T>一般适合复制语义类型,即实现了Copy,RefCell<T>适合移动语义类型
- Cell<T>无运行时开销,不会再运行时panic,RefCell则有运行时开销,会panic
写时复制Cow<T>
Cow<T>是一个枚举体智能指针,包括2个可选项:
- Borrowed:用于包裹引用
- Woned:用于包裹所有者
cow提供的功能是:以不可变的方式访问内容,在需要可变借用或所有权的时候再克隆一份数据。cow要点:
- Cow<T>实现了Deref,所以可以直接调用T的不可变方法
- 在需要修改T时,可以使用to_mut方法获取可变借用。该方法会克隆,且仅克隆一次。如果T本身有所有权,则调用to_mut不会发生克隆
- 在需要修改T时,也可以用into_owned方法来获取一个拥有所有权的对象。如果T是借用类型,则会发生克隆,并创建新的有所有权的对象。如果T是所有权对象,则会将所有权转移到新的克隆对象。