面试题答案
一键面试所有权(ownership)概念
- 基本原则:在Rust中,每一个值都有一个被称为其所有者(owner)的变量。值在任一时刻有且只有一个所有者。当所有者(变量)离开作用域,这个值将被丢弃。例如:
{
let s = String::from("hello"); // s 是 "hello" 这个字符串的所有者
} // 这里 s 离开了作用域,字符串 "hello" 被丢弃
- 内存管理机制:Rust通过所有权系统来管理内存,而无需像C++ 那样手动释放内存。对于简单数据类型(如
i32
),其值存储在栈上,当变量离开作用域时,栈上空间自动释放。对于复杂数据类型(如String
),数据存储在堆上,栈上存储指向堆数据的指针和长度等元数据。当所有者离开作用域,Rust会自动调用drop
函数释放堆上内存。
借用(borrowing)概念
- 定义:借用允许在不获取所有权的情况下使用值。有两种类型的借用:
- 不可变借用:使用
&
符号,例如let s1 = &s;
,这里s1
是对s
的不可变借用,不可变借用允许多个同时存在。 - 可变借用:使用
&mut
符号,例如let s2 = &mut s;
,可变借用只允许一个同时存在,以防止数据竞争。
- 不可变借用:使用
- 生命周期规则:借用有生命周期的概念,借用的生命周期必须足够短,以确保在借用结束前被借用的值不会被释放。
确保内存安全
- 防止悬空指针:由于值只有一个所有者,当所有者离开作用域值被释放,不存在其他指针指向已释放的内存,避免了悬空指针问题。
- 防止数据竞争:通过可变借用只能有一个,且不可变借用和可变借用不能同时存在的规则,防止多个指针同时读写数据导致的数据竞争问题。
违反借用规则导致编译错误场景
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);
}
通过在不同的作用域内创建不可变借用和可变借用,确保不可变借用和可变借用不会同时存在,从而满足借用规则。