Rust Ownership - Reference and Borrowing
·
Sricor
引用与借用
- 引用允许使用值但不获取其所有权
- the ownership system that makes sure references are always valid
- 引用的作用域是从声明的地方开始一直持续到最后一次使用为止
- 引用默认不允许修改引用的值,可使用
mut
关键字使其为可变引用- 在任意给定时间,要么只能有一个可变引用,要么只能有多个不可变引用
引用
引用(reference)是一个地址,我们可以由此访问储存于该地址的属于其变量的数据,创建一个引用的行为称为借用(borrowing)。
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize { // s 是 String 的引用
s.len()
} // 这里,s 离开了作用域。但因为它并不拥有引用值的所有权,所以什么也不会发生
calculate_length
函数以一个对象的引用作为参数而不是获取值的所有权。
&s1
语法让我们创建一个 指向 值 s1
的引用,但是并不拥有它,所以当引用停止使用时,它所指向的值也不会被丢弃。
可变引用
可变引用允许修改一个借用的值。
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world!");
}
在任意给定时间,同一作用域内,要么只能有一个可变引用,要么只能有多个不可变引用,这一限制防止同一时间对同一数据存在多个可变引用,在编译时就避免数据竞争。
// 以下代码无法通过编译
let mut s = String::from("hello");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
let r3 = &mut s; // 大问题
println!("{}, {}, and {}", r1, r2, r3);
可以使用大括号来创建一个新的作用域,以允许拥有多个可变引用。
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; // 没问题
println!("{} and {}", r1, r2);
// 此位置之后 r1 和 r2 不再使用
let r3 = &mut s; // 没问题
println!("{}", r3);
悬垂引用
悬垂指针是其指向的内存可能已经被分配给其它持有者。在 Rust 中编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域。
// 以下代码无法通过编译
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String { // dangle 返回一个字符串的引用
let s = String::from("hello"); // s 是一个新字符串
&s // 返回字符串 s 的引用
} // 这里 s 离开作用域并被丢弃。其内存被释放。
// 危险!
s
是在 dangle
函数内创建的,当 dangle
的代码执行完毕后,s
将被释放。不过我们尝试返回它的引用。这意味着这个引用会指向一个无效的 String
,无法通过编译。
fn no_dangle() -> String {
let s = String::from("hello");
s
}
这样就没有任何错误了,所有权被移动出去,所以没有值被释放。