Rust学习笔记(续):所有权和生命周期

本文最后更新于:2023年3月1日 凌晨

Rust学习笔记(续):所有权和生命周期

Rust 中的所有权和引用有什么作用?


在 Rust 中,所有权和引用是 Rust 语言最独特的特性之一,也是保障 Rust 内存安全的重要机制。所有权和引用的概念,可以简单理解为在 Rust 中管理内存的方式。

在 Rust 中,所有值都有一个被称为所有者(owner)的变量。当这个变量离开作用域时,这个值也会被销毁。这种方式被称为所有权(ownership),它确保了在编译时没有野指针(dangling pointers)、二次释放(double free)等内存安全问题。比如:

1
2
3
4
fn main() {
let s = String::from("hello"); // s 是 String 类型的变量,也是所有者
println!("{}", s); // 输出 hello
} // 这里 s 离开作用域,String 类型的值也会被销毁

在 Rust 中,变量可以通过引用(reference)来访问值,而不获取其所有权。引用是指向值的指针,但是它不会拥有值,而是只借用(borrow)值。借用分为可变借用和不可变借用两种。可变借用(mutable borrow)是对值进行修改的借用,只能有一个可变借用,不可变借用(immutable borrow)则可以有多个。比如:

1
2
3
4
5
6
7
8
9
fn main() {
let s = String::from("hello");
let len = calculate_length(&s); // 传递一个对 s 的引用
println!("The length of '{}' is {}.", s, len); // 这里再次使用 s,不需要获取所有权
}

fn calculate_length(s: &String) -> usize { // 函数接收一个 String 类型的引用
s.len()
}

以上代码中,calculate_length 函数接收一个 String 类型的引用 s,并返回其长度。在 main 函数中,我们用 &s 来传递 s 的引用,并获取其长度,最终再次使用 s 输出字符串。在这个过程中,main 函数并没有获取 s 的所有权,而是通过引用的方式使用它,这也保证了内存的安全性。

总之,在 Rust 中,所有权和引用机制是 Rust 语言独特的特性,它们能够在编译时保障内存安全性,使得 Rust 语言在系统编程和嵌入式领域有着广泛的应用。


生命周期是什么?在 Rust 中如何注明生命周期?


生命周期(lifetime)是 Rust 中的一个概念,用于描述变量、引用等的生存期。在 Rust 中,每个变量都有一个生命周期,它表示这个变量的有效范围,也就是在何时可以被访问,在何时应该被销毁。

Rust 的生命周期注解用于声明引用的生命周期,也可以用于泛型类型。生命周期注解使用单引号 ' 来标识,如 'a'b 等。注解的位置通常在变量名之后,以表示这个变量所引用的对象的生命周期。例如:

1
2
3
4
5
6
7
fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
if x > y {
x
} else {
y
}
}

上面的代码中,foo 函数有一个生命周期参数 'a,表示返回值的生命周期应该和参数 xy 的生命周期中最小的那个相同。也就是说,返回的引用必须满足它所指向的对象在 xy 中的最小生命周期内都是有效的。

生命周期注解通常在函数签名中使用,以帮助编译器推断引用的生命周期。使用生命周期注解可以避免一些常见的内存错误,例如悬垂指针(dangling pointer)和引用非法内存的情况。

需要注意的是,生命周期注解并不会改变变量本身的生命周期,它只是用于帮助编译器分析引用的生命周期。在 Rust 中,变量的生命周期由作用域和所有权规则来决定,这是 Rust 的另一个核心特性。


如何避免所有权问题?可以给出一个例子说明吗?


避免所有权问题的一种常见方法是使用引用,特别是使用不可变引用。通过使用引用,可以传递变量的值而不是拥有它的所有权,从而避免潜在的所有权问题。

例如,在 Rust 中,考虑以下代码:

1
2
3
4
5
6
7
8
9
fn main() {
let s = String::from("hello");
let len = calculate_length(s);
println!("The length of the string is {}.", len);
}

fn calculate_length(s: String) -> usize {
s.len()
}

这段代码创建了一个名为 sString 类型变量,并将其传递给 calculate_length 函数。但是,这里的问题是 s 的所有权被传递给了函数,因此在函数中不能再使用 s 变量。因此,当尝试调用 s.len() 时,编译器会报错。

要避免这个问题,可以将 calculate_length 函数的参数改为一个不可变引用。这样,可以传递变量的引用而不是它的所有权:

1
2
3
4
5
6
7
8
9
fn main() {
let s = String::from("hello");
let len = calculate_length(&s);
println!("The length of the string is {}.", len);
}

fn calculate_length(s: &String) -> usize {
s.len()
}

现在,calculate_length 函数的参数是一个指向 String 类型变量的不可变引用。由于该函数没有获得 s 的所有权,因此可以安全地在函数中访问 s 的属性和方法,而不会导致编译器错误。


Rust学习笔记(续):所有权和生命周期
https://jinbilianshao.github.io/2023/02/23/Rust学习笔记(续):问题解答/
作者
连思鑫
发布于
2023年2月23日
许可协议