生命周期基础
编译器通过生命周期来确保所有的借用都是合法的,典型的,一个变量在创建时生命周期随之开始,销毁
时生命周期也随之结束。
生命周期的范围
1. 🌟
/* 为 `i` 和 `borrow2` 标 注 合 适 的 生 命 周 期 范 围 */
// `i` 拥 有 最 长 的 生 命 周 期 , 因 为 它 的 作 用 域 完 整 的 包 含 了 `borrow1` 和 `borrow2` 。
// 而 `borrow1` 和 `borrow2` 的 生 命 周 期 并 无 关 联 , 因 为 它 们 的 作 用 域 没 有 重 叠
fn main() {
let i = 3;
{
let borrow1 = &i; // `borrow1` 生 命 周 期 开 始. ──┐
// │
println!("borrow1: {}", borrow1); // │
} // `borrow1` 生 命 周 期 结 束. ──────────────────────────────────┘
{
let borrow2 = &i;
println!("borrow2: {}", borrow2);
}
}
2. 🌟🌟
示例
{
let x = 5; // ----------+-- 'b
// |
let r = &x; // --+-- 'a |
// | |
println!("r: {}", r); // | |
// --+ |
} // ----------+
/* 像 上 面 的 示 例 一 样 , 为 `r` 和 `x` 标 准 生 命 周 期 , 然 后 从 生 命 周 期 的 角 度. */
fn main() {
{
let r; // ---------+-- 'a
// |
{ // |
let x = 5; // -+-- 'b |
r = &x; // | |
} // -+ |
// |
println!("r: {}", r); // |
} // ---------+
}
生命周期标注
Rust 的借用检查器使用显式的生命周期标注来确定一个引用的合法范围。但是对于用户来说,我们在大多
数场景下,都无需手动去标注生命周期,原因是编译器会在某些情况下自动应用生命周期消除规则。
在了解编译器使用哪些规则帮我们消除生命周期之前,首先还是需要知道该如何手动标记生命周期。
函数
大家先忽略生命周期消除规则,让我们看看,函数签名中的生命周期有哪些限制:
需要为每个引用标注上合适的生命周期
返回值中的引用,它的生命周期要么跟某个引用参数相同,要么是 'static
示例
// 引 用 参 数 中 的 生 命 周 期 'a 至 少 要 跟 函 数 活 得 一 样 久
fn print_one<'a>(x: &'a i32) {
println!("`print_one`: x is {}", x);
}
// 可 变 引 用 依 然 需 要 标 准 生 命 周 期
fn add_one<'a>(x: &'a mut i32) {
*x += 1;
}
// 下 面 代 码 中 , 每 个 参 数 都 拥 有 自 己 独 立 的 生 命 周 期 , 事 实 上 , 这 个 例 子 足 够 简 单 , 因 此 它 们 应 该 被
fn print_multi<'a, 'b>(x: &'a i32, y: &'b i32) {
println!("`print_multi`: x is {}, y is {}", x, y);
}
// 返 回 一 个 通 过 参 数 传 入 的 引 用 是 很 常 见 的 , 但 是 这 种 情 况 下 需 要 标 注 上 正 确 的 生 命 周 期
fn pass_x<'a, 'b>(x: &'a i32, _: &'b i32) -> &'a i32 { x }
fn main() {
let x = 7;
let y = 9;
print_one(&x);
print_multi(&x, &y);
let z = pass_x(&x, &y);
print_one(z);
let mut t = 3;
add_one(&mut t);
print_one(&t);
}
3. 🌟
/* 添 加 合 适 的 生 命 周 期 标 注 , 让 下 面 的 代 码 工 作 */
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {}
4. 🌟🌟🌟
/* 使 用 三 种 方 法 修 复 下 面 的 错 误 */
fn invalid_output<'a>() -> &'a String {
&String::from("foo")
}
fn main() {
}
5. 🌟🌟
// `print_refs` 有 两 个 引 用 参 数 , 它 们 的 生 命 周 期 `'a` 和 `'b` 至 少 得 跟 函 数 活 得 一 样 久
fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) {
println!("x is {} and y is {}", x, y);
}
/* 让 下 面 的 代 码 工 作 */
fn failed_borrow<'a>() {
let _x = 12;
// ERROR: `_x` 活 得 不 够 久does not live long enough
let y: &'a i32 = &_x;
// 在 函 数 内 使 用 `'a` 将 会 报 错 , 原 因 是 `&_x` 的 生 命 周 期 显 然 比 `'a` 要 小
// 你 不 能 将 一 个 小 的 生 命 周 期 强 转 成 大 的
}
fn main() {
let (four, nine) = (4, 9);
print_refs(&four, &nine);
// 这 里 ,four 和 nice 的 生 命 周 期 必 须 要 比 函 数 print_refs 长
failed_borrow();
// `failed_borrow` 没 有 传 入 任 何 引 用 去 限 制 生 命 周 期 `'a`, 因 此 , 此 时 的 `'a` 生 命 周 期 是
}
Structs
6. 🌟
/* 增 加 合 适 的 生 命 周 期 标 准 , 让 代 码 工 作 */
// `i32` 的 引 用 必 须 比 `Borrowed` 活 得 更 久
#[derive(Debug)]
struct Borrowed(&i32);
// 类 似 的 , 下 面 两 个 引 用 也 必 须 比 结 构 体 `NamedBorrowed` 活 得 更 久
#[derive(Debug)]
struct NamedBorrowed {
x: &i32,
y: &i32,
}
#[derive(Debug)]
enum Either {
Num(i32),
Ref(&i32),
}
fn main() {
let x = 18;
let y = 15;
let single = Borrowed(&x);
let double = NamedBorrowed { x: &x, y: &y };
let reference = Either::Ref(&x);
let number = Either::Num(y);
println!("x is borrowed in {:?}", single);
println!("x and y are borrowed in {:?}", double);
println!("x is borrowed in {:?}", reference);
println!("y is *not* borrowed in {:?}", number);
}
7. 🌟🌟
/* 让 代 码 工 作 */
#[derive(Debug)]
struct NoCopyType {}
#[derive(Debug)]
struct Example<'a, 'b> {
a: &'a u32,
b: &'b NoCopyType
}
fn main()
{
let var_a = 35;
let example: Example;
{
let var_b = NoCopyType {};
/* 修 复 错 误 */
example = Example { a: &var_a, b: &var_b };
}
println!("(Success!) {:?}", example);
}
8. 🌟🌟
#[derive(Debug)]
struct NoCopyType {}
#[derive(Debug)]
#[allow(dead_code)]
struct Example<'a, 'b> {
a: &'a u32,
b: &'b NoCopyType
}
/* 修 复 函 数 的 签 名 */
fn fix_me(foo: &Example) -> &NoCopyType
{ foo.b }
fn main()
{
let no_copy = NoCopyType {};
let example = Example { a: &1, b: &no_copy };
fix_me(&example);
println!("Success!")
}