九、Rust 高级特性
1. 泛型
-
Rust 中的泛型,和其他语言中的泛型概念是相同的,但也有一些 Rust 特有的特性
-
可以使用泛型的地方
- 函数
- 结构体
- 枚举
- 结构体/枚举的方法
-
Rust 结构体特有的特性
-
可推断性:结构体/枚举的泛型是可推断的
struct Point<T> { x: T, y: T, } let p1 = Point { x: 4, y: 5 }; // 可行 let p2 = Point { x: 4, y: 5.0 }; // 不可行,类型不兼容,与泛型不匹配 -
编译时单态化
- 单态化:编译时,Rust 使用具体类型,来将泛型代码转换为特定类型代码的过程
- Rust 通过在编译时进行泛型代码的 单态化(monomorphization)来保证效率
-
-
Rust 中特有的泛型:生命周期泛型
- 这个概念将在 3. 生命周期 中介绍
2. trait
-
作用:告诉 Rust 编译器,某个特定类型拥有可能与其他类型共享的功能
-
trait是将方法签名组合起来的一种方式trait中可以包含一些方法的默认实现- 目的: 定义一个实现某些目的所必需的行为的集合
- 个人理解:
trait的作用类似于Java/C++/Typescript等语言中的abstract class
-
作用域限制
-
只有当
trait或者要实现trait的类型位于crate的本地作用域时,才能为该类型实现trait -
基本用法
// 来自官方教程的一个示例 // 这是一个 public 的 trait,可以在其他模块中引入 pub trait Summary { // 这个方法只定义了签名,没有定义实现。实现这个 trait 的结构体应当实现这个方法 fn summarize_author(&self) -> String; // 这个方法有一个默认的实现,实现这个 trait 的结构体可以实现自己的同名方法,来覆盖这个默认的实现 fn summarize(&self) -> String { format!("(Read more from {}...)", self.summarize_author()) } } pub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String, } // 为 NewsArticle 结构体实现 Summary trait impl Summary for NewsArticle { // 这个方法没有默认实现,所以这里必须实现 fn summarize_author(&self) -> String { format!("@{}", self.author) } // 这个实现会覆盖默认实现 fn summarize(&self) -> String { format!("{}, by {} ({})", self.headline, self.author, self.location) } } pub struct Tweet { pub username: String, pub content: String, pub reply: bool, pub retweet: bool, } // 为 Tweet 结构体实现 Summary trait impl Summary for Tweet { // 因为 summarize 有一个默认实现,所以这里不是必须实现,可以省略 // summarize_author 没有默认实现,所以这里必须实现 fn summarize_author(&self) -> String { format!("@{}", self.username) } } fn notify(item: impl Summary) { println!("Breaking news! {}", item.summarize()); // 下一行代码会报错,因为 item 没有实现 std::fmt::Display // println!("Breaking news! {}", item); } -
高级用法
// 续上一个示例 // trait bound 语法 fn notify2<T: Summary>(item: T) {} fn notify3<T: Summary>(i1: T, i2: T) {} // 指定多个 trait bound use std::fmt::Display; fn notify4<T: Summary + Display>(item: T) { println!("Breaking news! {}", item); } // 来自官方教程的一个示例:使用 where 从句 use std::fmt::Debug; fn some_function<T, U>(t: T, u: U) -> i32 where T: Display + Clone, U: Clone + Debug, { return 1; } impl Tweet { /// 返回一个实现了指定 trait 的类型 /// /// * 支持: impl 结构体/泛型, 函数 /// * 不支持: impl trait /// /// 只有这一种写法,没有 trait bound 的写法 fn some_fn() -> impl Summary { return Tweet { username: String::from("horse_ebooks"), content: String::from("of course, as you probably already know, people"), reply: false, retweet: false, }; } } // 官方教程的一个示例 pub fn largest<T: PartialOrd + Copy>(list: &[T]) -> T { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } return largest; }
3. 生命周期
-
针对的目标:租借值(引用)
-
生命周期:引用保持有效的作用域
- “生命周期的概念某种程度上说不同于其他语言中类似的工具,毫无疑问这是 Rust 最与众不同的功能”
-
借用检查器 (borrow checker) :通过比较作用域来确保所有的借用都是有效的
-
生命周期语法注解,即生命周期泛型
- 注意:生命周期注解出现在签名中,但是不存在于函数体中的任何代码中
-
生命周期注解存在的地方
- 函数
- 包含引用的结构体
-
示例代码
// 函数中的生命周期注解 /// /// * 纯引用: &str /// * 可变纯引用: &mut str /// * 指定生命周期的引用: &'a str /// * 指定生命周期的可变引用: &'a mut str /// /// 所以说,尖括号内的不一定是类型泛型,也可能是生命周期注解 /// 注解 'a 中的 a 是任意合法命名即可 fn longer_str<'a>(s1: &'a str, s2: &'a str) -> &'a str { return match s1.len() > s2.len() { true => s1, false => s2, }; } // 包含引用的结构体中的生命周期注解 // &str 是一个引用,在结构体中定义这个类型时必须使用生命周期注解,其他类型的引用也必须这样 struct Person<'a> { name: &'a str, gender: u8, age: u8, } // 为带有生命周期注解的结构体实现方法时,也必须把生命周期注解带上 impl<'a> Person<'a> { fn get_gender(&self) -> &str { // 注意这里的返回值哦,是用的比较特殊的简写方式 match &self.gender { 0 => "girl", 1 => "boy", _ => "", } } pub fn introduce(&self) { let gender_str = self.get_gender(); println!( "{} is a {}, {} years old this year.", self.name, gender_str, self.age ); } } // 函数中的生命周期注解 fn get_man<'a>(name: &'a str) -> Person { Person { name, age: 0, gender: 1, } } // 函数中的生命周期注解 fn get_woman<'a>(name: &'a str) -> Person { Person { name, age: 0, gender: 0, } } -
生命周期的分类
- 输入生命周期:函数或方法的 参数 的生命周期
- 输出生命周期:函数或方法的 返回值 的生命周期
-
生命周期省略规则
- 每一个引用类型参数都有它自己的生命周期参数
- 多个引用类型参数的生命周期可以相同,也可以不同
- 如果只有一个输入生命周期参数,那么输出生命周期与此相同
- 如果方法有多个输入生命周期参数并且其中一个参数是
&self或&mut self,则表明这个方法是一个对象的方法(method),那么所有输出生命周期参数被赋予self的生命周期
- 每一个引用类型参数都有它自己的生命周期参数
-
特殊的生命周期:静态生命周期
- 注解:
'static - 能够存活于整个程序的运行期间
- 所有的字符串字面值的生命周期都是
'static
- 注解: