Rust精简笔记第2部分
- 继续Rust基础知识点总结,趁假期回顾学习
- 参考The Rust Programming Language & Rust in Action
八,泛型、Trait、生命周期
泛型:
- 函数定义中使用泛型
1 | fn largest<T>(list: &[T]) -> T { |
- 结构体定义中的泛型
1 | struct Point<T> { |
- 枚举定义中的泛型
1 | enum Option<T> { |
- 方法定义中的泛型
1 | struct Point<T> { |
Trait:
- 通过 trait 以一种抽象的方式定义共享的行为,trait 类似于其他语言中的接口,但也不完全一样.
1 | //定义 trait Summary ,定义summarize调取->summarize_author默认方法,达到调用默认行为,区分开实现trait的的定义 |
- trait 作为参数:
1 | // 方法接收是实现了 trait Summary的类型 |
- Trait Bound:
impl Trait 适用于短小的例子。trait bound 则适用于更复杂的场景,trait bound 与泛型参数声明在一起,位于尖括号中的冒号后面。
1 | //使用相同类型的trait可以转换成下边的更简单写法 |
- 通过
+指定多个 trait bound:
1 | pub fn notify<T: Summary + Display>(item: &T) {} |
通过
where简化 trait bound:每个泛型有其自己的 trait bound,所以有多个泛型参数的函数在名称和参数列表之间会有很长的 trait bound 信息,这使得函数签名难以阅读
1 | fn some_function<T, U>(t: &T, u: &U) -> i32 |
声明周期:
Rust 中的每一个引用都有其 生命周期(lifetime),也就是引用保持有效的作用域,Rust 编译器有一个借用检查器(borrow checker)它比较作用域来确保所有的借用都是有效的
函数签名中的生命周期注解:
1 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { |
- 参数声明周期使用方法,或者靠编译器提示添加。
1 | &i32 // 引用, 没有生命周期参数的 i32 的引用 |
结构体定义中的生命周期注解:
1
2
3
4
5
6
7
8
9
10struct ImportantExcerpt<'a> {
part: &'a str,
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
}静态生命周期:
生命周期能够存活于整个程序期间。所有的字符串字面值都拥有 ‘static 生命周期
1 | let s: &'static str = "I have a static lifetime."; |
九,集合:
vector:
- 类型是 Vec
在内存中彼此相邻地排列所有的值, vector 只能储存相同类型的值
1 | // Vec::new 创建 |
- 初始值来创建一个 Vec
:
1 |
|
- 读取 vector 的元素:
使用 &[index] 返回一个引用, 或者使用 get 方法以索引作为参数来返回一个 Option<&T>。
1 | fn main() { |
使用枚举来储存多种类型:
创建一个储存枚举值的 vector,这样最终就能够通过vector存储实际是不同类型的值了1
2
3
4
5
6
7
8
9
10
11
12fn main() {
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
}
HashMap
1 | let mut scores = HashMap::new(); |
十,函数、闭包、迭代器
函数:
- 函数的定义方式及在结构体实现里关联函数,关联函数与方法的使用区别
1 | use std::primitive; |
闭包:
- 闭包(closures)是可以保存在一个变量中或作为参数传递给其他函数的匿名函数。 闭包的定义以一对竖线(|)开始,在竖线中指定闭包的参数
1 | fn add_one_v1 (x: u32) -> u32 { x + 1 } //函数的定义 |
- 闭包会捕获其环境:
- 可以捕获其环境并访问其被定义的作用域的变量。如下边 x 并不是 equal_to_x 的一个参数,equal_to_x 闭包也被允许使用变量 x,因为它与 equal_to_x 定义于相同的作用域
1 | fn main() { |
当闭包从环境中捕获一个值,闭包会在闭包体中储存这个值以供使用,这会使用内存并产生额外的开销。
闭包可以通过三种方式捕获其环境,他们直接对应函数的三种获取参数的方式:获取所有权,可变借用和不可变借用。
1 | FnOnce 消费从周围作用域捕获的变量,闭包周围的作用域被称为其 环境,environment。为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移动进闭包。其名称的 Once 部分代表了闭包不能多次获取相同变量的所有权的事实,所以它只能被调用一次 |
由于所有闭包都可以被调用至少一次,所以所有闭包都实现了 FnOnce .大部分需要指定一个 Fn 系列 trait bound 的时候,可以从 Fn 开始,而编译器会根据闭包体中的情况告诉你是否需要 FnMut 或 FnOnce。
- 带有泛型和 Fn trait 的闭包:
可以创建一个存放闭包和调用闭包结果的结构体, 目的:结构体只会在需要结果时执行闭包,并会缓存结果值,再次调用闭包可以复用该值.
1 | struct Cacher<T> |
创建Cache的结构体,泛型T类型使用where 声明类型为闭包,结构体包含一个闭包,和一个用于存放闭包返回的值的u32类型,因为有可能第一次没有缓存,所有使用Option
- 官方完整例子:
1 | use std::thread; |
a.这样可以起到了使用结构体缓存了闭包执行的结果,会先从结构体里查找缓存的值,没有再计算。
b.同理也可以改造value的类型为HashMap, 可以通过key来找值,避免返回之前计算的始终同一个值。
iterator:
- 迭代器(iterator):负责遍历序列中的每一项和决定序列何时结束的逻辑。
1 | let v1 = vec![1, 2, 3]; |
- next 是 Iterator 实现者被要求定义的唯一方法
1 | let v1 = vec![1, 2, 3]; |
- 调用 map 方法创建一个新迭代器,接着调用 collect 方法消费新迭代器并创建一个 vector
1 | //next 一次返回迭代器中的一个项,封装在 Some 中,当迭代器结束时,它返回 None |
- 迭代器 iter()、iter_mut()、into_iter()区别:
- iter()返回的是值的不可变引用. 即&T
- iter_mut() 返回的是值的可变引用. 即&mut T
- into_iter() 返回的是T类型的值
1 | use core::num; |
- 实现Iterator trait 来创建自定义迭代器:
1 | struct Counter { |
- Rust里iterator的定义:
1 | pub trait Iterator { |