点击上方蓝字 江湖评谈设为关注/星标
前言
Rust三大权概念,锁死了类似于不安全代码造出的内存怪异等各种现象,以确保万无一失的安全代码构建。这就分别包括了:所有权,借用权,生命周期权。本篇分别看下。
权
一.所有权:每个值都有一个唯一的所有者,且每次只能有一个所有者。当所有者超出作用域时,值将被自动销毁。
fn main() {
let s1 = String::from("Hello"); // `s1` 是 `String` 的所有者
let s2 = s1; // `s2` 获得所有权,`s1` 不再有效
// println!("{}", s1); // 编译错误:`s1` 不再有效
println!("{}", s2); // 正确:`s2` 拥有所有权
}
在这个示例中,s1 和 s2 都是 String 类型,但因为所有权转移,s1 被移除,不能再使用。Rust 的所有权系统会在编译时检查这些所有权转移,从而避免内存错误。
二.借用权:借用允许你在不获得所有权的情况下访问数据。借用有两种形式:借用数据能修改,借用数据不能修改。
1.借用能修改
fn main() {
let mut s1 = String::from("Hello");
let s2 = &mut s1; // 可变借用
s2.push_str(", world!"); // 通过 `s2` 修改 `s1`
println!("{}", s2); // 输出:Hello, world!
}
2.借用不能修改
fn main() {
let s1 = String::from("Hello");
let s2 = &s1; // 不可变借用
println!("{}", s2); // 正确:通过 `s2` 访问 `s1` 的内容
println!("{}", s1); // 正确:`s1` 仍然有效
}
注意,在同一时间,你只能拥有一个可变借用,不能同时有可变借用和不可变借用。这是 Rust 用来保证数据一致性的规则。
3.借用规则冲突
fn main() {
let mut s1 = String::from("Hello");
let s2 = &s1; // 不可变借用
let s3 = &mut s1; // 编译错误:不能同时拥有不可变借用和可变借用
}
在上面的例子中,s2 是对 s1 的不可变借用,而 s3 是对 s1 的可变借用。Rust 会禁止这种同时借用的行为,避免数据竞争。
三.生命周期权:Rust 用来追踪引用在程序中的有效期的机制。它确保引用不会在其指向的数据销毁后继续存在。生命周期通常由编译器自动推导,但在某些情况下,你需要显式地指定生命周期。
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
fn main() {
let string1 = String::from("Hello");
let string2 = String::from("World");
let result = longest(&string1, &string2); // 生命周期标注
println!("The longest string is: {}", result);
}
在这个例子中,'a 是生命周期标注,表示 s1 和 s2 的生命周期相同,并且返回值的生命周期也与它们相同。Rust 编译器根据这些标注推导出所有引用的生命周期,确保没有悬挂引用。
生命周期推导,在很多情况下,Rust 可以推导出生命周期,而你不需要显式地标注它们。
示例:生命周期推导
fn main() {
let string1 = String::from("Hello");
let string2 = String::from("World");
let result;
{
let s1 = &string1;
let s2 = &string2;
result = longest(s1, s2); // Rust 自动推导生命周期
}
println!("The longest string is: {}", result); // 编译错误:生命周期不匹配
}
在这个例子中,s1 和 s2 都是局部变量,当它们超出作用域时,result 变得无效,因为 result 的生命周期依赖于 s1 和 s2 的生命周期,而它们已经不再有效。
四.组合示例:所有权、借用和生命周期
fn main() {
let string1 = String::from("Hello");
let string2 = String::from("World");
let result = longest(&string1, &string2);
println!("The longest string is: {}", result);
}
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
在这个示例中:s1 和 s2 是对字符串的引用,它们的生命周期由 Rust 编译器推导出来。longest 函数返回一个引用,它的生命周期与传入的参数相同,这就是生命周期 'a 的作用。
总结
所有权:Rust 的内存管理通过所有权系统确保每个值有且只有一个所有者,所有者超出作用域时会自动清理内存。
借用:Rust 支持通过不可变借用和可变借用来借用数据,同时保证了没有数据竞争和内存错误。
生命周期:生命周期帮助 Rust 确保引用不会超过数据的有效期,避免了悬挂引用问题。
这些特性结合起来,使得 Rust 能在保证内存安全和数据一致性的同时,避免垃圾回收的性能开销,提供了零成本的抽象和高效的系统编程能力。
往期精彩回顾