前言
本文以最短篇幅概括借用和借用规则的相关知识点。
借用
在 Rust 中,变量除了可以直接进行所有权转移,还可以借用。借用分为两种:只读借用(&
),和读写借用(&mut
)。所谓的借用和指针更多的是语义上的区别,标记该借用来的指针对内存没有“所有权”。“借用”概念是在编译阶段的静态检查中起作用。
需要弄清的是 mut 在不同场景下的意义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
struct T {
index: i32,
}
fn test_fn(param1: &T, param2: &mut T, param3: &T) { // param2 参数需要可变引用
if param3.index > 0 {
param2.index = param1.index;
}
}
fn main() {
let tp = T { index: 1 };
let mut tp2 = T { index: 0 }; // tp2 是可变的
let mut tp3 = T { index: 3 }; // tp3 是可变的
test_fn(&tp, &mut tp2, &tp3); // 读写借用可变变量 tp2 只读借用可变变量 tp3
}
|
当修饰 Self 时:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
struct T {
index: i32,
}
impl T {
fn test_self_fn(&self) {
println!("test self fn");
}
fn test_mut_self_fn(&mut self) {
println!("test mut self fn");
}
}
fn main() {
let tp = T { index: 1 };
let mut tp2 = T { index: 0 };
tp.test_self_fn();
//tp.test_mut_self_fn(); // 只有被 mut 修饰的变量才能调用 &mut self 的函数
tp2.test_self_fn();
tp2.test_mut_self_fn();
}
|
在 Rust 中还有一种叫做切片(Slice)的类型,利用它能够引用集合中的一段连续序列,而不是整个集合。
1
2
3
4
5
6
7
|
fn main() {
let str1 = String::from("abcdefghijklmn");
let c1 = [1, 2, 3, 4, 5, 6];
println!("{}", &str1[..3]);
println!("{:?}", &c1[1..2]);
}
|
需要注意字符串切片是按 byte 分的,以后再单开文章讲 utf8 字符串的处理。
借用规则
既然整出了个借用的概念,那么肯定就有对应的约束规则。
第一点:引用必须是有效的。最经典的反面例子就是返回局部变量的引用。
1
2
3
4
|
fn dangle() -> &String {
let s = String::from("hello");
&s
}
|
第二点:在同一时间只允许有一个可变引用或者多个只读引用。这里需要注意的是这里讲的是有效代码:
1
2
3
4
5
6
7
8
9
|
fn main() {
let mut str1 = String::from("adasdsa");
let str2 = &mut str1;
let str3 = &str1;
str1 += "adad";
println!("{}", str1);
}
|
上例中的 str2 和 str3 从未使用过,所以只会报 warning。
早期 Rust 对借用的检查比较死板,引入 NLL (Non Lexical Lifetime)以后可以更精细的调节变量的作用范围,对于一些不会产生安全问题的写法放宽了限制:
1
2
3
4
5
6
7
8
9
10
11
12
|
fn main() {
let mut data = vec!['a', 'b', 'c', 'd'];
let slice = &mut data[..];
for elem in slice {
elem.make_ascii_uppercase();
}
data.push('c');
println!("{:?}", data);
}
|
1
2
3
4
5
6
7
8
9
10
|
fn process_or_default() {
let mut map = ...;
let key = ...;
match map.get_mut(&key) {
Some(value) => process(value),
None => {
map.insert(key, V::default());
}
}
}
|