Rust的所有权系统是其最为重要的特性之一,它确保了内存安全和避免了常见的内存错误,例如数据竞争和空指针引用。在本教程中,我们将学习Rust的所有权系统的基本概念和高级特性。
在Rust中,每个值都有一个所有者。当值超出作用域时,所有者将释放其拥有的资源。这确保了内存资源的正确释放,避免了内存泄漏和悬空指针。
fn main() {
let s = String::from("hello");
println!("{}", s);
} // s超出作用域,String资源被释放
在上面的例子中,String
类型的变量s
拥有一个字符串值,并在超出main
函数作用域时释放了该值。
Rust中的所有权是通过移动语义来实现的,即将值从一个所有者转移到另一个所有者。当值被移动时,原所有者将失去对该值的所有权。
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1被移动到s2
println!("{}", s1); // 编译错误:值已被移动
}
在上面的例子中,String
类型的变量s1
被移动到变量s2
,因此尝试访问s1
将导致编译错误。
为了复制值而不是移动它,可以使用clone
方法:
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // s1被克隆到s2
println!("{}", s1); // 正常输出
}
在上面的例子中,String
类型的变量s1
通过克隆方法复制到变量s2
,因此s1
仍然保留对原始值的所有权。
为了临时借用值而不移动它,可以使用引用&
:
fn calculate_length(s: &String) -> usize {
s.len()
}
fn main() {
let s = String::from("hello");
let len = calculate_length(&s); // 借用s的引用
println!("Length of '{}' is {}", s, len);
}
在上面的例子中,函数calculate_length
借用了字符串s
的引用而不是移动它,因此s
仍然保留所有权。
如果需要修改借用的值,可以使用可变引用&mut
:
fn add_world(s: &mut String) {
s.push_str(" world");
}
fn main() {
let mut s = String::from("hello");
add_world(&mut s); // 可变借用s的引用
println!("{}", s); // 输出"hello world"
}
在上面的例子中,函数add_world
通过可变引用修改了字符串s
的值。
当函数返回一个引用时,需要指定引用的生命周期来确保引用不会超出作用域。生命周期描述了引用的有效范围。
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
fn main() {
let s1 = String::from("hello");
let result;
{
let s2 = String::from("world");
result = longest(&s1, &s2);
}
println!("Longest string is '{}'", result); // 编译错误:s2的引用超出作用域
}
在上面的例子中,函数longest
返回两个