如上所示,Owership让我们改变一个变量的值变得“复杂”,那能否像其他编程语言那样随意改变变量的值呢?答案是有的。
所有权系统允许我们通过“Borrowing”的方式达到这个目的。这个机制非常像其他编程语言中的“读写锁”,即同一时刻,只能拥有一个“写锁”,或只能拥有多个“读锁”,不允许“写锁”和“读锁”在同一时刻同时出现。当然这也是数据读写过程中保障一致性的典型做法。只不过Rust是在编译中完成这个(Borrowing)检查的,而不是在运行时,这也就是为什么其他语言程序在运行过程中,容易出现死锁或者野指针的问题。
通过&符号完成Borrowing:
fn main() {
let x: Vec<i32> = vec!(1i32, 2, 3);
let y = &x;
println!("x={:?}, y={:?}", x, y);
}
Borrowing(&x)并不会发生所有权moved,所以println可以同时访问x和y。 通过引用,就可以对普通类型完成修改。
fn main() {
let mut x: i32 = 100;
{
let y: &mut i32 = &mut x;
*y += 2;
}
println!("{}", x);
}
借用与引用的区别
借用与引用是一种相辅相成的关系,若B是对A的引用,也可称之为B借用了A。
很相近对吧,但是借用一词本意为要归还。所以在Rust用引用时,一定要注意应该在何处何时正确的“归回”借用/引用。 最后面的“高级”小节会详细举例。
规则
- 同一时刻,最多只有一个可变借用(&mut T),或者2。
- 同一时刻,可有0个或多个不可变借用(&T)但不能有任何可变借用。
- 借用在离开作用域后释放。
- 在可变借用释放前不可访问源变量。
可变性
Borrowing也分“不可变借用”(默认,&T)和“可变借用”(&mut T)。
顾名思义,“不可变借用”是只读的,不可更新被引用的内容。
fn main() {
let x: Vec<i32> = vec!(1i32, 2, 3);
//可同时有多个不可变借用
let y = &x;
let z = &x;
let m = &x;
//ok
println!("{:?}, {:?}, {:?}, {:?}", x, y, z, m);
}
再次强调下,同一时刻只能有一个可变借用(&mut T),且被借用的变量本身必须有可变性 :
fn main() {
//源变量x可变性
let mut x: Vec<i32> = vec!(1i32, 2, 3);
//只能有一个可变借用
let y = &mut x;
// let z = &mut x; //错误
y.push(100);
//ok
println!("{:?}", y);
//错误,可变借用未释放,源变量不可访问
// println!("{:?}", x);
} //y在此处销毁
高级例子
下面的复杂例子,进行了详细的注释,即使看不懂也没关系,可以在完成Lifetimes(生命周期)的学习后再仔细思考本例子。