向向量添加临时值时的生命周期

Lifetimes when adding temporary values to vectors

我在尝试了解 Rust 生命周期在某些情况下如何工作时遇到了一些问题,如下所示。我无法让它工作,但我不确定为什么。

struct Bar {
    value: &'static str,
}

struct Foo<'a, T: 'a> {
    bar: &'a T,
}

fn main() {
    let mut foos = Vec::new();

    let y = Bar {
        value: "Hello, world!",
    };
    let x = Foo { bar: &y };

    foos.push(x);
}
error[E0597]: `y` does not live long enough
  --> src/main.rs:15:25
   |
15 |     let x = Foo { bar: &y };
   |                         ^ borrowed value does not live long enough
...
18 | }
   | - `y` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

这是我实际要实现的目标的简化示例:

fn main() {
    let foos = vec![
        Foo { bar: &(Bar { value: "a" }) },
        Foo { bar: &(Bar { value: "b" }) },
    ];
}

如果有任何想法、想法或解释,我将不胜感激。

非词法生命周期后

由于 non-lexical lifetimes.

,您的代码的两个版本现在都可以使用

在非词法生命周期之前

你的问题可以简化为这个例子:

fn main() {
    let mut foos = Vec::new();
    let y = &42;
    foos.push(y);
}

在这种情况下要记住的重要一点是,变量的销毁顺序与它们的创建顺序相反。您几乎可以将代码视为

fn main() {
    let mut foos = Vec::new();
    {
        let y = &42;
        {
            foos.push(y);
        }
        // destroy y
    }}
    // destroy foos
}

对于像我展示的简单值,这并不重要,但当您有具有自定义 Drop 实现的复杂类型时,这就更重要了。

一个简单的解决方法是重新排序语句:

fn main() {
    let y = &42;
    let mut foos = Vec::new();
    foos.push(y);
}

现在,可以保证被引用的对象 比向量中存储的引用的寿命更长。对于您原来的简化示例,这有效:

let y = Bar { value: "Hello, world!" };
let x = Foo { bar: &y };
let mut foos = Vec::new();
foos.push(x);

您的原始代码有一些棘手的问题。我们来看看vec!宏的展开:

let foos = <[_]>::into_vec(Box::new([Foo { bar: &(Bar { value: "a" }) }]));

我们可以简化为

let foos = Box::new(&42);

问题是临时变量是临时。它仅在函数调用期间存在。这意味着对临时变量的引用不能持续更长时间。这就是错误消息提示 "consider using a let binding to increase its lifetime" 的原因。通过这样做,变量将比函数调用的寿命更长。

Is it possible to make the temporary value last longer without using a let statement? The vector will have many values, say 30. So I'll have to place 30 let statements?

不,你必须明确他们应该活多久,所以你需要明确他们在哪里。我看到两个解决方案:

  1. 更改您的结构,使其拥有项目,而不是引用它们:

    struct Foo<T> {
        bar: T,
    }
    
    let foos = vec![
        Foo { bar: Bar { value: "a" } },
        Foo { bar: Bar { value: "b" } },
    ];
    
  2. 创建一个拥有所有内部类型的向量,然后对其进行映射以获取引用:

    let bars = vec![Bar { value: "a" }, Bar { value: "b" }];
    let foos: Vec<_> = bars.iter().map(|bar| Foo { bar: bar }).collect();