MST

星途 面试题库

面试题:Rust类型系统之所有权与借用

请阐述Rust中所有权(ownership)和借用(borrowing)的概念,以及它们如何在类型系统中确保内存安全。并举例说明一个由于违反借用规则而导致编译错误的场景,以及如何修改代码来解决该问题。
23.5万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

所有权(ownership)概念

  1. 基本原则:在Rust中,每一个值都有一个被称为其所有者(owner)的变量。值在任一时刻有且只有一个所有者。当所有者(变量)离开作用域,这个值将被丢弃。例如:
{
    let s = String::from("hello"); // s 是 "hello" 这个字符串的所有者
} // 这里 s 离开了作用域,字符串 "hello" 被丢弃
  1. 内存管理机制:Rust通过所有权系统来管理内存,而无需像C++ 那样手动释放内存。对于简单数据类型(如i32),其值存储在栈上,当变量离开作用域时,栈上空间自动释放。对于复杂数据类型(如String),数据存储在堆上,栈上存储指向堆数据的指针和长度等元数据。当所有者离开作用域,Rust会自动调用drop函数释放堆上内存。

借用(borrowing)概念

  1. 定义:借用允许在不获取所有权的情况下使用值。有两种类型的借用:
    • 不可变借用:使用&符号,例如let s1 = &s;,这里s1是对s的不可变借用,不可变借用允许多个同时存在。
    • 可变借用:使用&mut符号,例如let s2 = &mut s;,可变借用只允许一个同时存在,以防止数据竞争。
  2. 生命周期规则:借用有生命周期的概念,借用的生命周期必须足够短,以确保在借用结束前被借用的值不会被释放。

确保内存安全

  1. 防止悬空指针:由于值只有一个所有者,当所有者离开作用域值被释放,不存在其他指针指向已释放的内存,避免了悬空指针问题。
  2. 防止数据竞争:通过可变借用只能有一个,且不可变借用和可变借用不能同时存在的规则,防止多个指针同时读写数据导致的数据竞争问题。

违反借用规则导致编译错误场景

fn main() {
    let mut s = String::from("hello");
    let r1 = &s;
    let r2 = &mut s; // 编译错误:不能在有不可变借用 r1 时创建可变借用 r2
    println!("{}, {}", r1, r2);
}

修改代码解决问题

fn main() {
    let mut s = String::from("hello");
    {
        let r1 = &s;
        println!("r1: {}", r1);
    } // r1 离开作用域,借用结束
    let r2 = &mut s;
    println!("r2: {}", r2);
}

通过在不同的作用域内创建不可变借用和可变借用,确保不可变借用和可变借用不会同时存在,从而满足借用规则。