1. 泛型
泛型就是一种多态,泛型参数必需在使用前进行声明。
1 | fn add<T>(a:T, b:T) -> T { |
1. 结构体中使用泛型
1 | struct Point<T> { |
2. 枚举中使用泛型
1 | enum Result<T, E> { |
3. 方法中使用泛型
1 | struct Point<T, U> { |
4. 泛型的性能
Rust 通过在编译时进行泛型代码的单态化(monomorphization)来保证效率。编译器会填充编译时使用的具体类型,寻找所有泛型代码被调用的位置并针对具体类型生成代码
2. 特征(Trait)
特征(Trait)类似于其他语言中的接口。使用 trait
关键字来声明一个特征,后面跟着特征名,在大括号中定义了该特征的所有方法,方法签名结尾是分号。
1 | pub trait Summary { |
1. 孤儿规则
如果你想要为类型 A 实现特征 T,那么 A 或者 T 至少有一个是在当前作用域中定义的。
2. 默认实现
可以在特征中定义具有默认实现的方法,其它类型也可以重载该方法。并且默认实现允许调用该特征中的其他方法,即使它们没有默认实现。
1 | pub trait Summary { |
3. 特征约束(trait bound)
可以使用特征作为函数参数,它是一个语法糖,叫做特征约束,其还可以指定多个约束条件。
1 | // 使用任何实现了 Summary 特征的类型作为该函数的参数 |
除了单个约束条件,还可以指定多个约束条件,并且使用 where
关键字简化。
1 | // 多重特征约束 |
4. 函数返回中的 impl Trait
可以通过 impl Trait 来说明一个函数返回了一个类型,该类型实现了某个特征。
1 | fn returns_summarizable() -> impl Summary { |
注意:
- 对于调用者而言,只知道返回了一个实现了 Summary 特征的对象,但不知道具体类型
- 该功能,在返回的真实类型非常复杂时,很有用
- 该功能,只能有返回一个具体的类型,如果想要实现返回不同的类型,需要使用特征对象
5. 派生特征
形如 #[derive(Debug)]
的代码,是一种特征派生语法,被标记的对象会自动实现对应的默认特征代码,继承相应的功能。
3. 生命周期
生命周期的主要作用是避免悬垂引用,它会导致程序引用了本不该引用的数据。为了保证所有权和借用的正确性,Rust 使用了一个借用检查器(Borrow checker),来检查我们程序的借用正确性。
1 | { |
生命周期简而言之就是引用的有效作用域,一般情况下,编译器会自动推导。在存在多个引用时,编译器有时会无法自动推导生命周期,此时就需要我们手动去标注,通过为参数标注合适的生命周期来帮助编译器进行借用检查的分析。
1 | fn main() { |
1. 语法
生命周期标注并不会改变任何引用的实际作用域。
- 语法以单引号
'
开头,名称往往是一个单独的小写字母 'a: 'b
是生命周期约束语法,表示'a
必须比'b
活得久,可以直接声明或者使用where 'a: 'b
- 特殊的生命周期
'static
,拥有该生命周期的引用可以和整个程序活得一样久,例如字符串字面量和特征对象
1 | &i32 // 一个引用 |
2. 函数中的生命周期
1 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { |
- 和泛型一样,使用生命周期参数,需要先声明
<'a>
- 表示
x、y、返回值
至少活得和'a
一样久
3. 结构体中的生命周期
在结构体中使用引用,需要为结构体中的每一个引用标注上生命周期。
1 | struct ImportantExcerpt<'a> { |
4. 方法中的生命周期
类似泛型参数语法,根据生命周期消除规则,方法签名中,往往不需要标注生命周期。
1 | struct ImportantExcerpt<'a> { |
5. 生命周期消除
编译器为了简化用户的使用,有时会进行生命周期消除。函数参数和返回值的生命周期被称为输入生命周期和输出生命周期。
有三条消除规则来确定哪些场景不需要显式地去标注生命周期,当不适用时,就会报错来提示手动标注。
1. 每一个引用参数都会获得独自的生命周期
例如一个引用参数的函数就有一个生命周期标注:fn foo<'a>(x: &'a i32)
,两个引用参数的有两个生命周期标注:fn foo<'a, 'b>(x: &'a i32, y: &'b i32)
, 依此类推。
2. 若只有一个输入生命周期,即函数参数中只有一个引用类型,那么该生命周期会被赋给所有的输出生命周期,也就是所有返回值的生命周期都等于该输入生命周期
例如函数 fn foo(x: &i32) -> &i32
,x
参数的生命周期会被自动赋给返回值 &i32
,因此该函数等同于 fn foo<'a>(x: &'a i32) -> &'a i32
。
3. 若存在多个输入生命周期,且其中一个是 &self
或 &mut self
,则 &self
的生命周期被赋给所有的输出生命周期
拥有 &self
形式的参数,说明该函数是一个方法,该规则让方法的使用便利度大幅提升。