rust 初探 – 引用和借用
引用和借用
- 参数的类型是 &String 而不是 String
- & 符号就表示引用:允许你引用某些值而不取得其所有权
我们看一个例子:
fn main() {
let s1 = String::from("hello");
let len = get_length(&s1); // 并不拥有s1 的所有权
println!("{} - {}", s1, len);
}
fn get_length(s: &String) -> usize {
s.len() // 根本没有获得对应的所有权
}
借用
定义:把引用作为函数参数这个行为就叫借用
如果需要改变借用的变量,需要将其定义为可变的,否则会报错
// fn main() {
// let s1 = String::from("hello");
// let len = get_length(&s1);
// println!("{} - {}", s1, len);
// }
// fn get_length(s: &String) -> usize {
// s.push_str(", cargo"); // 直接修改是不允许的
// s.len()
// }
fn main() {
// 1. 需要将变量声明为可变的
let mut s1 = String::from("hello");
// 2. 然后传入的也需要是一个可变的引用
let len = get_length(&mut s1);
println!("{} - {}", s1, len);
}
// 函数定义里面也需要是一个可变的引用
fn get_length(s: &mut String) -> usize {
s.push_str(", cargo");
s.len()
}
可变引用
- 限制1:在特定作用域内,对于某一块数据,只能有一个可变的引用(可在编译时防止数据竞争)
- 以下三种行为会发生数据竞争:
- 两个或多个指针同时访问同一个数据
- 至少有一个指针用于写入数据
- 没有使用任何机制来同步对数据的访问
- 可以通过创建新的作用域,来允许非同时的创建多个可变引用
fn main() {
let mut s = String::from("hello");
// let s1 = &mut s;
// println!("{}", s1);
// let s2 = &mut s;
// // error[E0499]: cannot borrow `s` as mutable more than once at a time
// println!("{}-{}", s1, s2);
let s1 = &mut s;
println!("{}", s1);
let s2 = &mut s; // 这里就是正常的,因为他们不是同一个作用域里,同时的可变引用了
println!("{}", s2);
}
- 限制2:不可以同时拥有一个可变引用和一个不变的引用(多个不变的引用时可以的)
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
let r3 = &mut s;
//error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
println!("{} {} {}", r1, r2, r3);
}
悬空引用(Dangling references)
悬空指针:一个指针引用了内存中的某个地址,而这块内存可能已经释放并分配给其他人使用了
在 rust 中,编译器可保证引用永远都不是悬空引用:如果你引用了某些数据,编译器会保证在引用离开作用域之前数据不会离开作用域
fn main() {
let r = dangle();
}
// error[E0106]: missing lifetime specifier
fn dangle() -> &String {
let s = String::from("hello");
&s
}
引用的规则
- 在任何给定的时刻,只能满足下列条件之一:
- 一个可变的引用
- 任意数量不可变的引用
- 引用必须一直有效