书籍
【Rust 秘典(死灵书)】 可参考如何实现一个 Vec 【Rust 圣经】
借用规则
Rust 的借用规则是这样的:
- 你可以有许多不可变引用(只读)。
- 或者,你可以有一个可变引用(读/写)。
- 但是你不能同时拥有可变引用和不可变引用。
Rust 的借用规则仍然适用,因为这些规则旨在防止数据竞争和其他并发问题,即使在单线程上下文中也是如此。
生命周期
Rust 在函数或方法的签名中有一套生命周期参数的速记(省略)规则,这可以使生命周期的使用更为便捷。这些规则被编译器应用,所以在编写代码时,你只需要在需要更明确的生命周期时提供生命周期参数即可。
以下是这些速记规则:
-
每个引用的参数都有自己的生命周期参数。例如,一个函数的签名
fn foo<'a, 'b>(x: &'a i32, y: &'b i32)
中,x
和y
各有自己的生命周期'a
和'b
。 -
如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数。例如,在
fn foo<'a>(x: &'a i32) -> &'a i32
,x
和返回值都拥有相同的生命周期'a
。 -
如果方法有多个输入生命周期参数并且其中一个参数是
&self
或&mut self
,那么所有输出生命周期参数被赋予self
的生命周期。这使得方法可以很容易地输出其它引用到与接收者相关的数据。
速记规则适用于大多数常见的情况。当生命周期需要更明确或者规则不适用时,必须使用显式的生命周期注解。
生命周期约束
生命周期约束可以根据需求以多种方式编写。举一些例子:
-
T: 'a
: 如果T
是某种引用,那么T
的生命周期必须至少和'a
一样长。这也可以用于泛型结构体,表示所有引用类型的数据成员必须至少存活'a
那么久。 -
T: Trait + 'a
: 对于某种实现了Trait
的类型T
,如果T
是引用类型,那么T
的生命周期必须至少和'a
一样长。 -
'b: 'a
: 生命周期'b
至少和生命周期'a
一样长。
生命周期标注帮助Rust编译器理解引用的有效范围和相互关系,以此来保证内存安全并防止悬垂引用等问题。不过大部分时候Rust可以自动推断出正确的生命周期,你不必显式指定。显式生命周期主要在编译器无法自己推断时使用,例如在某些涉及到泛型的复杂函数或方法中。
使用元组结构体和 Deref 定义新类型
实现 Deref 特征后, 可以自动做一层类似类型转换的操作, 可以将 NewString 变成 String 来使用。 这样就会像直接使用 String 那样去使用 NewString, 而无需为每一个操作都添加上 self.0。
同时, 如果不想 NewString 暴露底层数组的所有方法, 我们还可以为 NewString 去重载这些方法, 实现隐藏的目的
use std::ops::Deref;
struct NewString(String);
impl Deref for NewString {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl NewString {
fn reversed(&self) -> String {
self.0.chars().rev().collect()
}
}
fn main() {
let hello = NewString(String::from("hello"));
println!("This call String method size: {}, reversed: {}", hello.len(), hello.reversed());
}