(五)Rust引用和借用

本文最后更新于 2024年6月27日 下午

函数的参数(参数的数据未全部在栈上存储)会转移所有权,Rust中引用传参可以避免所有权转移。引用像是一个指针,它提供了数据存储的地址,通过寻址便能获取实际数据,这和C++语言中的引用传参类似。下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let s1 = String::from("hello");

let len = calculate_length(&s1);

println!("The length of '{s1}' is {len}.");
}

fn calculate_length(s: &String) -> usize {
s.len()
}

&s1传入calculate_length,与此同时,在函数定义中,使用&String代替String&符号代表引用,它允许在函数体中引用某些值但是无需获取所有权。下面图可以帮助理解各个变量关系:

这和C++中的引用非常相似,如果你有C++编程背景,理解Rust中的引用就很容易了。

根据C++的经验思考一个问题:引用在内部可以修改其所指代的实际内容吗?下面看一个例子:

1
2
3
4
5
6
7
8
9
10
// !!该代码块无法通过编译!!
fn main() {
let s = String::from("hello");

change(&s);
}

fn change(some_string: &String) {
some_string.push_str(", world");
}

不幸的是,代码无法通过编译,会得到如下错误:error[E0596]: cannot borrow *some_string as mutable, as it is behind a & reference

Rust中变量默认是不可变的,引用也是如此,因此无法修改所引用的内容。如果允许修改需要使用可变引用。

可变引用

如何使用可变引用?下面是一个例子:

1
2
3
4
5
6
7
8
9
fn main() {
let mut s = String::from("hello");

change(&mut s);
}

fn change(some_string: &mut String) {
some_string.push_str(", world");
}

首先将s更改为mut。在调用change函数时使用&mut创建一个可变引用,同时更新函数形参为some_string: &mut String。如此,change函数中可以对引用内容修改。

可变引用有一个限制:如果一个值有一个可变引用,那么它将不能再有其他引用。下面是一个例子:

1
2
3
4
5
6
7
// !!该代码块无法通过编译!!
let mut s = String::from("hello");

let r1 = &mut s;
let r2 = &mut s;

println!("{}, {}", r1, r2);

Rust是根据引用在代码中最后使用位置,判断其是否违反限制规则,下面是一个合法的例子:

1
2
3
4
5
6
7
8
9
let mut s = String::from("hello");

let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{r1} and {r2}");
// 变量r1 和 r2 在此之后不再使用

let r3 = &mut s; // no problem
println!("{r3}");

总结:对同一值来说,其非可变引用的使用范围不能与其可变引用的使用范围相交;其可变引用范围内不能包含其它引用。

使用范围:定义引用一行作为起始,最后使用该引用一行作为终止,其中认定为该引用的使用范围。

可以依据上述规则可以尝试不同情况,这里不再赘述。

悬空引用

在其余使用指针的语言中,悬空指针(指针指向内存无效)错误很容易发生。在Rust中,编译器保证所有的引用不会存在悬空引用:编译器保证数据总是后于引用被释放。下面是悬空引用的一个例子,编译器会阻止程序编译:

1
2
3
4
5
6
7
8
9
10
// !!该代码块无法通过编译!!
fn main() {
let reference_to_nothing = dangle();
}

fn dangle() -> &String {
let s = String::from("hello"); // 创建一个新的字符串

&s // 返回字符串s的引用
} // s离开作用域,内存被释放。

总结

  1. 在任意时刻,代码中可以存在一个可变引用或者多个不可变引用。
  2. 引用一定有效。

参考

Rust docs


(五)Rust引用和借用
https://www.happyallday.cn/Rust/RustLang_05_ReferencesAndBorrowing/
作者
DevoutPrayer
发布于
2024年6月27日
更新于
2024年6月27日
许可协议