借用
多数情况下,我们更希望能访问数据,同时不取得其所有权。为实现这点,Rust 使用了借用(borrowing)机制。对象可以通过引用(&T)来传递,从而取代通过值(T)来传递。
编译器(通过借用检查)静态地保证了引用总是指向有效的对象。也就是说,当存在引用指向一个对象时,该对象不能被销毁。
// 此函数取得一个 box 的所有权并销毁它
fn eat_box_i32(boxed_i32: Box<i32>) {
println!("Destroying box that contains {}", boxed_i32);
}
// 此函数借用了一个 i32 类型
fn borrow_i32(borrowed_i32: &i32) {
println!("This int is: {}", borrowed_i32);
}
fn main() {
// 创建一个装箱的 i32 类型,以及一个存在栈中的 i32 类型。
let boxed_i32 = Box::new(5_i32);
let stacked_i32 = 6_i32;
// 借用了 box 的内容,但没有取得所有权,所以 box 的内容之后可以再次借用。
// 译注:请注意函数自身就是一个作用域,因此下面两个函数运行完成以后,
// 在函数中临时创建的引用也就不复存在了。
borrow_i32(&boxed_i32);
borrow_i32(&stacked_i32);
{
// 取得一个对 box 中数据的引用
let _ref_to_i32: &i32 = &boxed_i32;
// 报错!
// 当 `boxed_i32` 里面的值之后在作用域中被借用时,不能将其销毁。
eat_box_i32(boxed_i32);
// 改正 ^ 注释掉此行
// 在 `_ref_to_i32` 里面的值被销毁后,尝试借用 `_ref_to_i32`
//(译注:如果此处不借用,则在上一行的代码中,eat_box_i32(boxed_i32)可以将 `boxed_i32` 销毁。)
borrow_i32(_ref_to_i32);
// `_ref_to_i32` 离开作用域且不再被借用。
}
// `boxed_i32` 现在可以将所有权交给 `eat_i32` 并被销毁。
//(译注:能够销毁是因为已经不存在对 `boxed_i32` 的引用)
eat_box_i32(boxed_i32);
}
可变性
可变数据可以使用 &mut T 进行可变借用。这叫做可变引用(mutable reference),它使借用者可以读/写数据。相反,&T 通过不可变引用(immutable reference)来借用数据,借用者可以读数据而不能更改数据:
#[allow(dead_code)]
#[derive(Clone, Copy)]
struct Book {
// `&'static str` 是一个对分配在只读内存区的字符串的引用
author: &'static str,
title: &'static str,
year: u32,
}
// 此函数接受一个对 Book 类型的引用
fn borrow_book(book: &Book) {
println!("I immutably borrowed {} - {} edition", book.title, book.year);
}
// 此函数接受一个对可变的 Book 类型的引用,它把年份 `year` 改为 2014 年
fn new_edition(book: &mut Book) {
book.year = 2014;
println!("I mutably borrowed {} - {} edition", book.title, book.year);
}
fn main() {
// 创建一个名为 `immutabook` 的不可变的 Book 实例
let immutabook = Book {
// 字符串字面量拥有 `&'static str` 类型
author: "Douglas Hofstadter",
title: "Gödel, Escher, Bach",
year: 1979,
};
// 创建一个 `immutabook` 的可变拷贝,命名为 `mutabook`
let mut mutabook = immutabook;
// 不可变地借用一个不可变对象
borrow_book(&immutabook);
// 不可变地借用一个可变对象
borrow_book(&mutabook);
// 可变地借用一个可变对象
new_edition(&mut mutabook);
// 报错!不能可变地借用一个不可变对象
new_edition(&mut immutabook);
// 改正 ^ 注释掉此行
}
别名使用
数据可以多次不可变借用,但是在不可变借用的同时,原始数据不能使用可变借用。或者说,同一时间内只允许一次可变借用。仅当最后一次使用可变引用之后,原始数据才可以再次借用。
struct Point { x: i32, y: i32, z: i32 }
fn main() {
let mut point = Point { x: 0, y: 0, z: 0 };
let borrowed_point = &point;
let another_borrow = &point;
// 数据可以通过引用或原始类型来访问
println!("Point has coordinates: ({}, {}, {})",
borrowed_point.x, another_borrow.y, point.z);
// 报错!`point` 不能以可变方式借用,因为当前还有不可变借用。
// let mutable_borrow = &mut point;
// TODO ^ 试一试去掉此行注释
// 被借用的值在这里被重新使用
println!("Point has coordinates: ({}, {}, {})",
borrowed_point.x, another_borrow.y, point.z);
// 不可变的引用不再用于其余的代码,因此可以使用可变的引用重新借用。
let mutable_borrow = &mut point;
// 通过可变引用来修改数据
mutable_borrow.x = 5;
mutable_borrow.y = 2;
mutable_borrow.z = 1;
// 报错!不能再以不可变方式来借用 `point`,因为它当前已经被可变借用。
// let y = &point.y;
// TODO ^ 试一试去掉此行注释
// 报错!无法打印,因为 `println!` 用到了一个不可变引用。
// println!("Point Z coordinate is {}", point.z);
// TODO ^ 试一试去掉此行注释
// 正常运行!可变引用能够以不可变类型传入 `println!`
println!("Point has coordinates: ({}, {}, {})",
mutable_borrow.x, mutable_borrow.y, mutable_borrow.z);
// 可变引用不再用于其余的代码,因此可以重新借用
let new_borrowed_point = &point;
println!("Point now has coordinates: ({}, {}, {})",
new_borrowed_point.x, new_borrowed_point.y, new_borrowed_point.z);
}
ref 模式
在通过 let 绑定来进行模式匹配或解构时,ref 关键字可用来创建结构体/元组的字段的引用。下面的例子展示了几个实例,可看到 ref 的作用:
#[derive(Clone, Copy)]
struct Point { x: i32, y: i32 }
fn main() {
let c = 'Q';
// 赋值语句中左边的 `ref` 关键字等价于右边的 `&` 符号。
let ref ref_c1 = c;
let ref_c2 = &c;
println!("ref_c1 equals ref_c2: {}", *ref_c1 == *ref_c2);
let point = Point { x: 0, y: 0 };
// 在解构一个结构体时 `ref` 同样有效。
let _copy_of_x = {
// `ref_to_x` 是一个指向 `point` 的 `x` 字段的引用。
let Point { x: ref ref_to_x, y: _ } = point;
// 返回一个 `point` 的 `x` 字段的拷贝。
*ref_to_x
};
// `point` 的可变拷贝
let mut mutable_point = point;
{
// `ref` 可以与 `mut` 结合以创建可变引用。
let Point { x: _, y: ref mut mut_ref_to_y } = mutable_point;
// 通过可变引用来改变 `mutable_point` 的字段 `y`。
*mut_ref_to_y = 1;
}
println!("point is ({}, {})", point.x, point.y);
println!("mutable_point is ({}, {})", mutable_point.x, mutable_point.y);
// 包含一个指针的可变元组
let mut mutable_tuple = (Box::new(5u32), 3u32);
{
// 解构 `mutable_tuple` 来改变 `last` 的值。
let (_, ref mut last) = mutable_tuple;
*last = 2u32;
}
println!("tuple is {:?}", mutable_tuple);
}