引用和借用
什么是引用,在这个第二篇中提到了字符串,表现呢在于存放在stack上的ptr
.在rust中回去引用使用的是&
符号 ,可以让你使用值但原来变量的所有权不发生move,与使用 &
引用相反的操作是 解引用(dereferencing),它使用解引用运算符,*
获取引用的过程如下所示 let zzz =String::from("hello"); let aaa=&zzz;
这个就叫做借用。
和变量一样 引用也是默认不可变的 如果要通过引用修改原变量的值,则需要创建可变引用 使用mut
关键字 可变引用只能应用于可变变量
fn test_change_reference() {
let mut s1 = String::from("hello");
change(&mut s1);
// let r1 = &mut s;
// let r2 = &mut s;
// println!("{}, {}", r1, r2);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
println!("The is {}.", some_string);
}
可变引用有一个很大的限制:在同一时间只能有一个对某一特定数据的可变引用。这些代码编译时提示
cannot find value
sin this scop . help: a local variable with a similar name exists:
这个限制的好处是 Rust 可以在编译时就避免数据竞争。有点类似在跑了go test -race XXXX
。
竞态条件
-
两个或更多指针同时访问同一数据。
-
至少有一个指针被用来写入数据。
-
没有同步数据访问的机制。
数据竞争会导致未定义行为,难以在运行时追踪,并且难以诊断和修复;Rust 避免了这种情况的发生,因为它甚至不会编译存在数据竞争的代码! -
可以使用大括号来创建一个新的作用域,以允许拥有多个可变引用,只是不能同时拥有
fn main() {
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1 在这里离开了作用域,所以我们完全可以创建一个新的引用
let r2 = &mut s;
}
- 同时使用可变与不可变引用
let mut s = String::from("hello");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
let r3 = &mut s; // 有问题
println!("{}, {}, and {}", r1, r2, r3);
针对一个变量可以多次借用,因为变量和引用都不可变,针对可变变量的 不可变引用和可变引用的生命周期到可变引用出现的代码行。
悬垂引用(Dangling References)
在具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针(dangling pointer),所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。相比之下,在 Rust 中编译器确保引用永远也不会变成悬垂状态
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s
}
这个方法主要是返回字符串 s 的引用,但是方法执行完毕 s 离开作用域并被丢弃。其内存被释放。
所以S的引用无法得到,是个无效的String Rust在编译阶段就抛出,
总结
- 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
- 引用必须总是有效的。