引用和所有权借用
所有权不仅可以转移(原变量会丢失数据的所有权),还可以通过引用的方式来借用数据的所有权(borrow ownership)。
使用引用借用变量所有权时,【借完】之后会自动交还所有权,从而使得原变量不丢失所有权。至于什么时候【借完】,尚无法在此深究。
例如:
fn main(){
{
let s = String::from("hello");
let sf1 = &s; // 借用
let sf2 = &s; // 再次借用
println!("{}, {}, {}",s, sf1, sf2);
} // sf2离开,sf1离开,s离开
}
注意,&s表示创建变量s的引用,为某个变量创建引用的过程不会转移该变量所拥有的所有权。

(不可变)引用实现了Copy Trait,因此下面的代码是等价的:
#![allow(unused)]
fn main() {
// 多次创建s的引用,并将它们赋值给不同变量
let sf1 = &s;
let sf2 = &s;
// 拷贝sf1,使得sf2也引用s,
// 但sf1是引用,是可Copy的,因此sf1仍然有效,即仍然指向数据
let sf1 = &s;
let sf2 = sf1;
}
还可以将变量的引用传递给函数的参数,从而保证在调用函数时变量不会丢失所有权。
fn main(){
let s = String::from("hello");
let s1 = s.clone();
// s1丢失所有权,s1将回到未初始化状态
f1(s1);
// println!("{}", s1);
// 传递s的引用,借用s所有权
let l = f2(&s);
// 交还所有权
// s仍然可用
println!("{} size: {}", s, l);
}
fn f1(s: String){
println!("{}", s);
}
fn f2(s: &String)->usize{
s.len() // len()返回值类型是usize
}
可变引用和不可变引用的所有权规则
变量的引用分为可变引用&mut var和不可变引用&var,站在所有权借用的角度来看,可变引用表示的是可变借用,不可变引用表示的是不可变借用。
- 不可变借用:借用只读权,不允许修改其引用的数据
- 可变引用:借用可写权(包括可读权),允许修改其引用的数据
- 多个不可变引用可共存(可同时读)
- 可变引用具有排他性,在有可变引用时,不允许存在该数据的其他可变和不可变引用
- 这样的说法不准确,短短几句话也无法描述清楚,因此留在后面再详细解释
前面示例中f2(&s)传递的是变量s的不可变引用&s,即借用了数据的只读权,因此无法在函数内部修改其引用的数据值。
如要使用可变引用去修改数据值,要求:
- var的变量可变,即
let mut var = xxx - var的引用可变,即
let varf = &mut var
例如:
fn main(){
let mut x = String::from("junmajinlong");
let x_ref = &mut x; // 借用s的可写权
x_ref.push_str(".com");
println!("{}", x);
let mut s = String::from("hello");
f1(&mut s); // 借用s的可写权
println!("{}", s);
}
fn f1(s: &mut String){
s.push_str("world");
}
容器集合类型的所有权规则
前面所介绍的都是标量类型的所有权规则,此处再简单解释一下容器类型(比如tuple/array/vec/struct/enum等)的所有权。
容器类型中可能包含栈中数据值(特指实现了Copy的类型),也可能包含堆中数据值(特指未实现Copy的类型)。例如:
#![allow(unused)]
fn main() {
let tup = (5, String::from("hello"));
}
容器变量拥有容器中所有元素值的所有权。
因此,当上面tup的第二个元素的所有权转移之后,tup将不再拥有它的所有权,这个元素将不可使用,tup自身也不可使用,但仍然可以使用tup的第一个元素。
#![allow(unused)]
fn main() {
let tup = (5, String::from("hello"));
// 5拷贝后赋值给x,tup仍有该元素的所有权
// 字符串所有权转移给y,tup丢失该元素所有权
let (x, y) = tup;
println!("{},{}", x, y); // 正确
println!("{}", tup.0); // 正确
println!("{}", tup.1); // 错误
println!("{:?}", tup); // 错误
}
如果想要让原始容器变量继续可用,要么忽略那些没有实现Copy的堆中数据,要么clone()拷贝堆中数据后再borrow,又或者可以引用该元素。
#![allow(unused)]
fn main() {
// 方式一:忽略
let (x, _) = tup;
println!("{}", tup.1); // 正确
// 方式二:clone
let (x, y) = tup.clone();
println!("{}", tup.1); // 正确
// 方式三:引用
let (x, ref y) = tup;
println!("{}", tup.1); // 正确
}