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

4

我在尝试理解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" }) },
    ];
}

我很感激任何想法、思路或解释。

1个回答

1

非词法生命周期之后

由于非词法生命周期的存在,您的代码的两个版本现在都可以工作。

非词法生命周期之前

您的问题可以简化为以下示例:

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);

问题在于临时变量是临时的。它只存在于函数调用的持续时间内。这意味着对临时变量的引用不能持续更长时间。这就是为什么错误消息建议"考虑使用let绑定来增加其生命周期"。通过这样做,变量将比函数调用更长寿。

不使用let语句是否可以使临时值保留更长时间?向量将有许多值,比如30个。那么我需要放置30个let语句吗?

不,您必须明确说明它们应该存活多长时间,因此需要明确说明它们所在的位置。我看到两种解决方案:

  1. Change your structures so that they own items, instead of referring to them:

    struct Foo<T> {
        bar: T,
    }
    
    let foos = vec![
        Foo { bar: Bar { value: "a" } },
        Foo { bar: Bar { value: "b" } },
    ];
    
  2. Create a vector that owns all the inner types, then map over it to get references:

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

有没有办法在不使用let语句的情况下使临时值持续更长时间?向量将具有许多值,比如30个。那我就得放30个let语句吗?有什么想法吗? - caio
另一个问题:是否可能从函数返回 foos?例如:让 foos2: Vec<_> = foos()。我尝试了类似于 "fn foos<T>() -> Vec<Foo<T>> { }" 的东西,但它说有类型不匹配的问题。 - caio
@caio 这取决于你有哪个版本。 拥有的版本应该没问题,第二个(带地图的)不行。那个有对超出函数调用生存期的项目的引用。如果需要更多细节,建议提出另一个高级别的问题! - Shepmaster
请返回一个包含通用值的向量。参考链接:http://stackoverflow.com/questions/29581855/how-to-return-a-vector-containing-generic-values - caio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接