3.4 悬垂引用及其避免方法
听
悬垂引用(dangling reference)是指一个指针或引用指向的内存已经被释放或无效,但程序仍试图通过该引用访问数据。在 C 或 C++ 等语言中,这类错误常见且危险,可能导致程序崩溃、数据损坏或安全漏洞。
Rust 的设计目标之一就是在编译期彻底杜绝悬垂引用。它通过所有权和生命周期规则确保引用的生命周期不会超过其所指向值的生命周期。
为什么悬垂引用是危险的?
考虑以下伪代码(在 C 语言中可能合法,但在 Rust 中不允许):
char* dangling() {
char s[] = "hello";
return s; // 返回局部数组的地址 —— 函数返回后,s 已被销毁
}
调用 dangling() 会得到一个指向已释放栈内存的指针,后续使用将导致未定义行为。
Rust 如何防止悬垂引用?
Rust 编译器在类型检查阶段会分析每个引用的有效范围。如果发现引用可能超出其指向数据的存活时间,就会报错。
例如,尝试在 Rust 中编写类似逻辑:
fn dangling_reference() -> &String {
let s = String::from("hello");
&s // 错误:无法返回局部变量的引用
}
编译器会拒绝这段代码,并提示:
error[E0106]: missing lifetime specifier
更根本的问题是:s 在函数结束时离开作用域并被释放,而返回的引用试图指向一块已经无效的内存。Rust 不允许这种情况发生。
正确的做法
要返回字符串数据,应转移所有权,而不是返回引用:
fn no_dangle() -> String {
let s = String::from("hello");
s // 返回所有权,而非引用
}
这样,调用者获得 String 的所有权,内存由其负责管理,不存在悬垂问题。
借用中的作用域匹配
即使在同一函数内,也要确保引用的作用域不超出其数据的作用域:
let r;
{
let x = 5;
r = &x; // 借用 x
} // x 在此处被销毁
println!("{}", r); // 错误:r 是悬垂引用
编译器会指出 x 的生命周期不够长,无法满足 r 的使用需求。
小结
悬垂引用是内存安全的重大隐患,而 Rust 通过静态生命周期检查,在编译阶段就将其完全排除。开发者无需依赖运行时检查或手动跟踪内存状态,只需遵循借用规则,即可写出无悬垂引用的安全代码。这种“编译时报错而非运行时崩溃”的机制,正是 Rust 内存安全保障的核心体现之一。
#Rust 入门教程
分享于 1 周前