Paper

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
}

这样就没有任何错误了,所有权被移动出去,所以没有值被释放。