rust使用 与符号 &
表示引用。允许我们使用一个变量的值,而避免该变量所有值的转移。
fn main() {
let s1 = String::from("hello");
let len = calculate_len(&s1);
println("The length of '{}' is {}", s1, len);
}
fn calculate_len(s: &String) -> usize {
s.len()
}
我们将使用引用作为函数参数的行为称为借用。借用很形象的表达函数所使用的参数的所有权是不属于函数本身的,函数只是暂时借用了此变量的值。 当超出函数作用域后,引用失效。该值不会被销毁,因为该值得所有者仍然存在。
如果一个声明的引用是不可变的,则不允许改变该引用所指向的值。如果引用是可变的,且该引用指向的值是可变类型,则该可变引用允许改变它指向的值。
fn main() {
let mut s = String::from("hello"); // s is mutable
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world"); // some_string is Mutable reference
// so it can change the value of s
}
对于特定范围内的特定数据块,只能有一个可变引用。
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("r1: {}, r2: {}", r1, r2); // 在一个范围内同时使用了两个可变引用
}
error[E0499]: cannot borrow `s` as mutable more than once at a time
--> src/main.rs:4:14
|
3 | let r1 = &mut s;
| ------ first mutable borrow occurs here
4 | let r2 = &mut s;
| ^^^^^^ second mutable borrow occurs here
5 |
6 | println!("r1: {}, r2: {}", r1, r2);
| -- first borrow later used here
error: aborting due to previous error
如下面代码,分别在各自的作用域内使用是可以的:
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
println!("r1: {}", r1);
r1.push_str(", world");
let r2 = &mut s;
println!("r2: {}", r2);
}
// r1: hello
// r2: hello, world
rust对可变引用的限制避免数据竞争。数据竞争会导致程序出现未知的行为,且在运行时难以追踪定位、诊断和修复bug。以下三种行为发生时就会发生数据竞争:
同样,结合可变与不可变引用,以下代码也会出现错误:
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; // big problem
因为在同一作用域内,同一变量的可变引用,会导致无法预料不可变引用指向的值是否会突然改变。rust在编译时会阻止这种行为。但是扎起同一作用域内的多个不可变引用是被允许的,因为它们是不可变的,不会引起数据竞争。
垂悬引用是指一个指针引用的内存位置可能已经给了其他指针。在有指针的编程语言中,通过释放一些内存,同时保留指向该内存的指针,很容易错误的创建一个悬空指针。
相反,在Rust中,编译器保证引用永远不会是悬空引用:如果你有一个对某些数据的引用,编译器将确保数据不会在数据引用超出作用域之前超出作用域。
尝试创建一个悬空指针:
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> & String {
let s = String::from("hello"); // s is valid
&s // &s is valid
} // s go out of scope, s will be dealloacted.
error[E0106]: missing lifetime specifier
--> src/main.rs:5:16
|
5 | fn dangle() -> & String {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime
|
5 | fn dangle() -> &'static String {
| ^^^^^^^^
error: aborting due to previous error
错误说明是这个函数的返回类型包含了一个借用值,但是却无值可借用。引用要保证始终具有有效性。
解决方法是直接返回s:
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> & String {
let s = String::from("hello"); // s is valid
s // s is valid
} // s go out of scope, but ownership is moved out, and nothing deallocated
通过以上对于引用的学习和理解,总结如下: