MST

星途 面试题库

面试题:Rust所有权与借用检查器

描述Rust借用检查器如何利用所有权机制来确保内存安全。当借用规则被违反时,编译器会给出什么样的错误提示?请结合具体代码示例说明如何分析和解决这类错误。
21.5万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

Rust借用检查器利用所有权机制确保内存安全的方式

  1. 所有权规则基础
    • 单一所有权:在Rust中,每个值都有一个唯一的所有者。例如:
    let s = String::from("hello");
    
    这里 s 是字符串 hello 的所有者。当 s 离开作用域时,内存会被自动释放。
    • 所有权转移:所有权可以转移。比如:
    let s1 = String::from("hello");
    let s2 = s1;
    // 此时s1不再有效,因为所有权转移到了s2
    
  2. 借用检查器工作原理
    • 借用规则
      • 同一时间内,要么只能有一个可变借用,要么可以有多个不可变借用,但不能同时存在可变和不可变借用。
      • 借用的生命周期必须小于等于被借用对象的生命周期。
    • 例如:
    let mut s = String::from("hello");
    let r1 = &s; // 不可变借用
    let r2 = &s; // 另一个不可变借用,合法
    // let r3 = &mut s; // 错误,因为已经存在不可变借用r1和r2
    
    借用检查器在编译时会检查这些规则,确保在运行时不会出现悬空指针、数据竞争等内存安全问题。

借用规则违反时编译器的错误提示

当借用规则被违反时,编译器会给出详细的错误信息。例如,违反可变和不可变借用共存规则:

let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s;

编译器错误提示类似:

error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
 --> src/main.rs:4:13
  |
3 |     let r1 = &s;
  |              - immutable borrow occurs here
4 |     let r2 = &mut s;
  |             ^^^^^^^ mutable borrow occurs here
5 | }
  | - immutable borrow ends here

错误信息指出不能可变借用 s,因为已经存在不可变借用 r1

结合具体代码示例分析和解决这类错误

  1. 示例代码
fn main() {
    let mut numbers = vec![1, 2, 3];
    let first = &numbers[0];
    numbers.push(4);
    println!("The first number is: {}", first);
}
  1. 错误分析
    • 这里先对 numbers 进行了不可变借用 first,然后又尝试对 numbers 进行可变操作 push。这违反了借用规则,因为可变操作可能会使不可变借用 first 指向无效内存。
    • 编译器错误提示:
    error[E0502]: cannot borrow `numbers` as mutable because it is also borrowed as immutable
     --> src/main.rs:4:13
      |
    3 |     let first = &numbers[0];
      |              - immutable borrow occurs here
    4 |     numbers.push(4);
      |             ^^^^^^^^ mutable borrow occurs here
    5 |     println!("The first number is: {}", first);
      |                                        ---- immutable borrow later used here
    
  2. 解决方法
    • 方法一:调整操作顺序
    fn main() {
        let mut numbers = vec![1, 2, 3];
        numbers.push(4);
        let first = &numbers[0];
        println!("The first number is: {}", first);
    }
    
    先进行可变操作 push,再进行不可变借用,这样就不会违反借用规则。
    • 方法二:提前结束借用
    fn main() {
        let mut numbers = vec![1, 2, 3];
        {
            let first = &numbers[0];
            println!("The first number is: {}", first);
        }
        numbers.push(4);
    }
    
    通过将不可变借用放在一个单独的作用域内,当作用域结束时,借用结束,之后就可以对 numbers 进行可变操作。