Rust生命周期

Rust生命周期详解

一些规则

  1. &T 类型实现自动实现了Copy trait,所以可以直接拷贝
  2. &mut T 类型没有实现了Copy trait
  3. T 类型可以表示:&T 和 &mut T 和 T 类型
  4. 形变规则:
Type 在 ‘a 上的型变 在 T 上的型变
&'a T 协变的 协变的
&'a mut T 协变的 不变的
*const T 协变的
*mut T 不变的
[T][T; n] 协变的
fn() -> T 协变的
fn(T) -> () 逆变的
fn(T) -> T 不变的
std::cell::UnsafeCell<T> 不变的
std::marker::PhantomData<T> 协变的
dyn Trait<T> + 'a 协变的 不变的

例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
struct Foo<'a, 'b> {
    a: &'a str,
    b: &'b str,
}

fn main() {
    let a = String::from("hello");
    let b = String::from("world");
    let foo = Foo { a: &a, b: &b };
    println!("{}, {}", foo.a, foo.b);
}

生命周期省略规则

函数上的生命周期省略规则

  1. 参数中省略的每个生命周期类型参数都会(被推断)成为一个独立的生命周期类型参数。
  2. 如果参数中只使用了一个生命周期(省略或不省略都行),则将该生命周期作为所有省略的输出生命周期类型参数。
  3. 如果接受者(receiver)类型为 &Self 或 &mut Self,那么对 Self 的引用的生命周期会被作为所有省略的输出生命周期类型参数。

默认的 trait对象的生命周期

如果将 trait对象用作泛型类型的类型参数,则首先使用此容器泛型来尝试为此 trait对象推断一个约束(来替代那个假定的生命周期)。

  1. 如果存在来自此容器泛型的唯一约束,则该约束就为此 trait对象的默认约束
  2. 如果此容器泛型有多个约束,则必须指定一个约显式束为此 trait对象的默认约束

如果这两个规则都不适用,则使用该 trait对象的声明时的 trait约束:

  1. 如果原 trait 声明为单生命周期约束,则此 trait对象使用该约束作为默认约束。
  2. 如果 ‘static 被用做原 trait声明的任一一个生命周期约束,则此 trait对象使用 ‘static 作为默认约束。
  3. 如果原 trait声明没有生命周期约束,那么此 trait对象的生命周期会在表达式中根据上下文被推断出来,在表达式之外直接用 ‘static。

例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
struct S;

fn f1<'a, 'b>(rb: &'b &'a S) -> &'a S {
    *rb
}

fn f2<'a, 'b>(rb: &'b mut &'a S) -> &'a S {
    *rb
}

fn f3<'a, 'b>(rb: &'b &'a mut S) -> &'a S {
    *rb
}

fn f4<'a, 'b>(rb: &'b mut &'a mut S) -> &'a S {
    *rb
}

fn f5<'a, 'b>(rb: &'b mut &'a mut S) -> &'a mut S {
    *rb
}

分析:

  • f1: *rb 的生命周期是 'c S,又因为返回的是 &'a S,又因为‘c S实现了 Copy trait,可以直接拷贝,所以没问题。
  • f2: *rb 的生命周期是 'c S,又因为返回的是 &'a S,又因为‘c S实现了 Copy trait,可以直接拷贝,所以没问题。
  • f3: *rb 的生命周期是 'c mut S,又因为返回的是 &'a S,又因为‘c mut S没有实现Copy trait,不能直接拷贝,所以存在问题。
    • 解决方案:添加生命周期约束:<‘a, ‘b:‘a>, 从&'b &'a mut S可以看出,'b 必须是 'a 的子生命周期,即:'a: 'b, 添加'b:'a后天可以判断出'a='b, 又因为 *rb 的生命周期是 'c mut S,隐含了 'c: 'b, 所以 &'a S'c mut S 的子生命周期,所以可以解决问题。
  • f4: *rb 的生命周期是 'c S,又因为返回的是 &'a S,又因为‘c S没有实现Copy trait,不能直接拷贝,所以存在问题,解决方案同f3
  • f5: *rb 的生命周期是 'c S,又因为返回的是 &'a mut S,又因为‘c S没有实现 Copy trait,所以不能直接拷贝,所以存在问题,解决方案同f3
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
fn fetch<'a, 'b>(trace_id: &'a str, span_id: &'b str) -> Box<dyn Future<Output = ()>> {
    Box::new(async move {
        println!("{}", trace_id);
        println!("{}", span_id);
    })
}

fn fetchv2<'a, 'b>(trace_id: &'a str, span_id: &'b str) -> Box<impl Future<Output = ()>> {
    Box::new(async move {
        println!("{}", trace_id);
        println!("{}", span_id);
    })
}

分析:

  • fetch: 编译失败。对于动态分发会遵循:如果原 trait声明没有生命周期约束,那么此 trait对象的生命周期会在表达式中根据上下文被推断出来,在表达式之外直接用 ‘static。即它的生命周期为Box<dyn Future<Output = ()> + 'static>,即可添加生命周期约束<'a, 'b: 'a>解决

  • fetchv2: 编译成功。对于静态分发,编译器会根据上下文推断出生命周期,所以不需要添加生命周期约束。

0%