何时在结构中定义多个生命周期是有用的?

时间:2015-04-25 05:29:43

标签: rust lifetime

在Rust中,当我们想要一个包含引用的结构时,我们通常会定义它们的生命周期:

struct Foo<'a> {
    x: &'a i32,
    y: &'a i32,
}

但是也可以为同一结构中的不同引用定义多个生命周期:

struct Foo<'a, 'b> {
    x: &'a i32,
    y: &'b i32,
}

什么时候这样做有用?有人可以提供一些示例代码,这些代码在生命周期为'a时都不会编译,但在生命周期为'a'b时会编译(反之亦然)吗?

3 个答案:

答案 0 :(得分:13)

在熬夜之后,我能够想出一个生命时间重要的案例。这是代码:

static ZERO: i32 = 0;

struct Foo<'a, 'b> {
    x: &'a i32,
    y: &'b i32,
}

fn get_x_or_zero_ref<'a, 'b>(x: &'a i32, y: &'b i32) -> &'a i32 {
    if *x > *y {
        return x
    } else {
        return &ZERO
    }
}

fn main() {
    let x = 1;
    let v;
    {
        let y = 2;
        let f = Foo { x: &x, y: &y };
        v = get_x_or_zero_ref(&f.x, &f.y);
    }
    println!("{}", *v);
}

如果您要将Foo的定义更改为:

struct Foo<'a> {
    x: &'a i32,
    y: &'a i32,
}

然后代码将无法编译。

基本上,如果你想在任何需要它的参数具有不同生命周期的函数上使用struct的字段,那么struct的字段也必须具有不同的生命周期。

答案 1 :(得分:4)

这是另一个简单的示例,其中struct定义必须使用两个生存期才能按预期进行操作。它不会将聚合划分为不同生命周期的字段,而是将结构与另一个结构嵌套在一起。

struct X<'a>(&'a i32);

struct Y<'a, 'b>(&'a X<'b>);

fn main() {
    let z = 100;
    //taking the inner field out of a temporary
    let z1 = ((Y(&X(&z))).0).0;  
    assert!(*z1 == z);
}

结构Y有两个生存期参数,一个用于其包含的字段&X,一个用于X的包含字段&z

在操作((Y(&X(&z))).0).0中,X(&z)被创建为临时变量并被借用。它的生存期仅在此操作的范围内,在语句结束时到期。但是,由于X(&z)的生存期与其包含的字段&z不同,因此可以返回&z进行操作,该值可以稍后在函数中访问。

如果对Y结构使用单个生存期。该操作将不起作用,因为&z的生存期与其包含的结构X(&z)相同,并在语句末尾到期;因此返回的&z不再有效,以后再访问。

请参阅playground中的代码。

答案 2 :(得分:2)

我想在这里重新回答我的问题,因为它仍然出现在搜索结果中的高位,我觉得我可以更好地解释。考虑以下代码:

Rust Playground

struct Foo<'a> {
    x: &'a i32,
    y: &'a i32,
}

fn main() {
    let x = 1;
    let v;
    {
        let y = 2;
        let f = Foo { x: &x, y: &y };
        v = f.x;
    }
    println!("{}", *v);
}

和错误:

error[E0597]: `y` does not live long enough
--> src/main.rs:11:33
|
11 |         let f = Foo { x: &x, y: &y };
|                                 ^^ borrowed value does not live long enough
12 |         v = f.x;
13 |     }
|     - `y` dropped here while still borrowed
14 |     println!("{}", *v);
|                    -- borrow later used here

这是怎么回事?

  1. f.x 的生命周期要求至少足够大以包含 x 的范围,直到 println! 语句(因为它使用 &x 和然后分配给v)。
  2. Foo 的定义指定 f.xf.y 使用相同的通用生命周期 'a,因此 f.y 的生命周期必须至少为大到 f.x
  3. 但是,这行不通,因为我们将 &y 分配给 f.y,而 yprintln! 之前超出了范围。错误!

这里的解决方案是允许 Foof.xf.y 使用单独的生命周期,我们使用多个通用生命周期参数来实现:

Rust Playground

struct Foo<'a, 'b> {
    x: &'a i32,
    y: &'b i32,
}

现在 f.xf.y 的生命周期不再联系在一起。编译器仍将使用在 println!f.x 语句之前有效的生命周期。但是不再要求 f.y 使用相同的生命周期,因此编译器可以自由地为 f.y 选择更小的生命周期,例如仅对 y 的范围有效的生命周期.