Rust 程序设计语言(第二版)学习摘要
1.array是分配在栈上的
2.实现了Drop的类型不能实现Copy
3.所有元素都是Copy类型的元组也是Copy类型
4.函数参数传递和返回值都会发生作用域转移
5.有且只有一个可变引用,不可变引用可以多个,而且两者二选一
6.函数内不能返回局部变量的引用
7.slice就是没有所有权的引用
8.字符串字面值就是slice
9.当拥有某值的不可变引用时,就不能再获取一个可变引用
10.结构体存储引用,需要指定生命周期
11.{}告诉 println!使用被称为Display的格式
13.在{}中加入:?指示符告诉println!使用Debug的输出格式,{:#?}会加强可读性
14.自动引用和解引用:object.something()调用方法时,Rust会自动增加&、&mut或*以便使object符合方法的签名
15.关联函数:不使用self作为参数,即类函数
16.if let即是match的一个语法糖
17.mod和文件系统: ├── src │ ├── client.rs │ ├── lib.rs │ └── network │ ├── mod.rs │ └── server.rs 有子模块用文件夹+mod.rs,没有子模块直接xxx.rs 18.私有可见性规则:
1.如果一个项是公有的,它能被任何父模块访问
2.如果一个项是私有的,它能被其直接父模块及其任何子模块访问 19.使用super在层级中上移到当前模块的上一级模块,即父目录
20.vector.get返回的是Option<&T>,不能对可变vector获取可变引用,然后push: let mut v = vec![1, 2, 3, 4, 5]; let first = &v[0]; v.push(6); 21.遍历vector:
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
}
22.HashMap拥有key-value的所有权,如果是引用要保证声明周期有效,遍历:
for (key, value) in &scores {
println!("{}: {}", key, value);
}
23.异常:unwrap和expect,?只能用于返回Result的函数
24.Trait Bounds:
25.方法中,多个参数都没有生命周期标注,返回值生命周期同self
26.闭包定义会为每个参数和返回值推断一个具体类型;可以捕获其环境并访问其被定义的作用域的变量
27.当闭包从环境中捕获一个值,闭包会在闭包体中储存这个值以供使用。这会使用内存并产生额外的开销
28.Fn:从其环境不可变的借用值;FnMut:可变的借用值可以改变其环境;FnOnce:会移动环境变量到闭包内,所以只能被调用一次
29.move关键字不能应用于整形,因为整形可以被拷贝而不是移动
30.Box
31.Ref
32.Deref trait允许智能指针结构体实例表现的像引用一样
33.Drop trait允许我们自定义当智能指针离开作用域时运行的代码
34.为了启用*运算符的解引用功能,可以实现Deref trait。
35.输入*y时,Rust事实上在底层运行了如下代码:*(y.deref())
36.deref方法返回值的引用,以及*(y.deref())括号外边的普通解引用仍为必须的原因在于所有权
37.Rust在发现类型和trait实现满足三种情况时会进行解引用强制多态:
当T:Deref<Target=U> 时从&T到&U。
当T:DerefMut<Target=U> 时从&mut T到&mut U。
当T:Deref<Target=U> 时从&mut T到&U。 38.通过Deref trait将智能指针当作常规引用处理
39.通过std::mem::drop提早丢弃值
40.Rc
41.如下为选择Box
- Rc
允许相同数据有多个所有者;Box 和RefCell 有单一所有者。 - Box
允许在编译时执行不可变(或可变)借用检查;Rc 仅允许在编译时执行不可变借用检查;RefCell 允许在运行时执行不可变(或可变)借用检查。 - 因为 RefCell
允许在运行时执行可变借用检查,所以我们可以在即便 RefCell 自身是不可变的情况下修改其内部的值。
42.使用RefCell
43.RefCell
44.如果有一个储存了RefCell
45.避免引用循环:将Rc
46.Rust标准库只提供了1:1线程模型实现,即一个OS线程对应一个语言线程
47.thread::spawn的返回值类型是JoinHandle,调用其join会阻塞当前线程直到handle所代表的线程结束
48.不要共享内存来通讯;而是要通讯来共享内存
49.send函数获取其参数的所有权并移动这个值归接收者所有
50.RefCell
51.Send trait表明类型的所有权可以在线程间传递,任何完全由Send的类型组成的类型也会自动被标记为Send。几乎所有基本类型都是Send的,除了裸指针
52.Sync trait 表明一个实现了Sync的类型可以安全的在多个线程中拥有其值的引用。即,如果Send(&T),则T必须是实现Sync trait。和Send类似,基本类型是Sync的,完全Sync的类型组成的类型也是Sync的。
53.结构体自身被标记为pub,但是在结构体内部的字段默认是私有的。
54.如果一个语言必须有继承才能被称为面向对象语言的话,那么Rust就不是面向对象的。
55.一个trait只有同时满足如下两点时才被认为是对象安全的:
- trait不要求Self是Sized的,trait对象不是Sized的
- 所有的trait方法都是对象安全的 要求Self是Sized的,或者 满足如下三点: 必须不包含任何泛型类型参数 其第一个参数必须是Self类型或者能解引用为Self的类型(也就是说它必须是一个方法而非关联函数,并且以self、&self或&mut self作为第一个参数) 必须不能在方法签名中除第一个参数之外的地方使用Self
56.可能用到模式的地方:
match分支:穷尽性和默认模式 _
if let表达式
while let
for循环
let语句
函数参数:fn print_coordinates(&(x, y): &(i32, i32))
57.所有模式语法: 1.字面量
match x {
1 => println!("one"),
}
2.命名变量 ```Rust
match x {
Some(50) => println!("Got 50"),
Some(y) => println!("Matched, y = {:?}", y),
_ => println!("Default case, x = {:?}", x),
} ```
3.多种模式 ```Rust
match x {
1 | 2 => println!("one or two"),
} ```
4.通过...匹配值的范围 ```Rust
1 ... 5 => println!("one through five"), ```
5.解构并提取值 ```Rust
let p = Point { x: 0, y: 7 };
let Point { x, y } = p;
let Point { x: a, y: b } = p;
match p {
Point { x, y: 0 } => println!("On the x axis at {}", x),
Point { x: 0, y } => println!("On the y axis at {}", y),
Point { x, y } => println!("On neither axis: ({}, {})", x, y),
}
let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 }); ```
6.忽略模式中的值
用_忽略整个值 ```Rust
fn foo(_: i32) {
} ```
用一个嵌套_忽略部分值 ```Rust
let x = Some(5);
match x {
Some(_) => println!("got a Some and I don't care what's inside"),
None => (),
} ```
通过在名字前以一个下划线开头来忽略不使用的变量 ```Rust
let _x = 5 ```
用..忽略剩余的值 ```Rust
match origin {
Point { x, .. } => println!("x is {}", x),
} ```
7.用ref和ref mut在模式中创建引用
在模式的上下文中,& 匹配一个引用并返回它的值,而 ref 匹配一个值并返回一个引用。
当你匹配一个模式时, 模式匹配的变量会被绑定到一个值,会把值转移进match
模式中使用&会匹配已存在的引用中的值 ```Rust
let points = vec![
Point { x: 0, y: 0 },
Point { x: 1, y: 5 },
Point { x: 10, y: -3 },
];
let sum_of_squares: i32 = points
.iter()
.map(|&Point {x, y}| x * x + y * y)
.sum();
let mut robot_name = Some(String::from("Bors"));
match robot_name {
Some(ref mut name) => *name = String::from("Another name"),
None => (),
}
println!("robot_name is: {:?}", robot_name); ```
8.用了匹配守卫的额外条件
在模式后面指定一个额外的if条件来往匹配分支中引入匹配守卫
9.@绑定 ```Rust
match msg {
Message::Hello { id: id @ 3...7 } => {
println!("Found an id in range: {}", id)
},
} ```
58.在unsafe代码块中! 它们是: 1.解引用原生指针 2.调用一个不安全的函数或方法 3.访问或修改一个不可变的静态变量 4.实现一个不安全的trait
57.unsafe不会关掉借用检查器也不会禁用其它的Rust安全性检查,而是让编译器因内存安全性而没去检查的上述四个特性。
58.生命子周期语法:’b:’a声明一个不短于’a的生命周期’b
59.生命周期约束: struct Ref<’a, T>(&’a T); 因为T可以是任意类型,T自身也可能是一个引用,或者是一个存放了一个或多个引用的类型,而他们各自可能有着不同的生命周期,Rust不能确认T会与’a存活的一样久 所以要声明泛型T的生命周期约束: struct Ref<’a, T: ‘a>(&’a T);
60.在T上增加’static生命周期约束 来限制T为只拥有’static引用或没有引用的类型,没有任何引用的类型被算作 T: ‘static。
61.生命周期与 trait 对象必须遵守的规则: trait 对象的默认生命周期是 ‘static。 如果有 &’a X 或 &’a mut X,则默认(生命周期)是 ‘a。 如果只有 T: ‘a, 则默认是 ‘a。 如果有多个类似 T: ‘a 的从句,则没有默认值;必须明确指定。
62.关联类型只需要指定一次类型,泛型就要多次指定具体类型
63.运算符重载和默认类型参数
trait Add<RHS=Self> {
type Output;
fn add(self, rhs: RHS) -> Self::Output;
}
默认参数类型主要用于如下两个方面:
扩展类型而不破坏现有代码。
允许以一种大部分用户都不需要的方法进行自定义。
64.完全限定语法与消歧义
fn main() {
let b = Baz;
b.f();
<Baz as Foo>::f(&b);
<Baz as Bar>::f(&b);
}
65.newtype 模式用以在外部类型上实现外部 trait,类似C#的扩展方法
66.类型别名用来创建同义类型:type Kilometers = i32;
67.从不返回的 !,never type
fn bar() -> ! {
}
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
impl<T> Option<T> {
pub fn unwrap(self) -> T {
match self {
Some(val) => val,
None => panic!("called `Option::unwrap()` on a `None` value"),
}
}
}
continue和panic!的值是! ```Rust
loop {
print!("and ever ");
} ```
在loop中使用了!类型
68.函数指针实现了所有三个闭包 trait(Fn、FnMut 和 FnOnce)
69.使用Box情形:Box是一个指针,确定存储大小,当Box对象离开作用域,其在堆中分配的对象会自动释放掉。 a. 当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型值的时候 b. 当有大量数据并希望在确保数据不被拷贝的情况下转移所有权的时候 c. 当希望拥有一个值并只关心它的类型是否实现了特定 trait 而不是其具体类型的时候
70.函数和方法的隐式解引用强制多态
71.
a. Rc
72.当创建不可变和可变引用时,我们分别使用 & 和 &mut 语法。对于 RefCell
RefCell
73.结合 Rc