【问题标题】:Lifetimes when adding temporary values to vectors向向量添加临时值时的生命周期
【发布时间】:2015-04-11 16:34:56
【问题描述】:

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

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

【问题讨论】:

    标签: rust lifetime


    【解决方案1】:

    在非词法生命周期之后

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

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

    是否可以在不使用 let 语句的情况下使临时值持续更长时间?向量会有很多值,比如 30。所以我必须放置 30 个 let 语句?

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

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

    【讨论】:

    • 是否可以在不使用 let 语句的情况下使临时值持续更长时间?向量会有很多值,比如 30。所以我必须放置 30 个 let 语句?有什么想法吗?
    • 还有一个问题:是否可以从函数中返回foos?例如:让 foos2: Vec<_> = foos()。我尝试了类似 "fn foos() -> Vec> { }" 但它说类型不匹配。
    • @caio 这取决于您拥有的变体。拥有的版本应该没问题,第二个(带地图)不是。那个引用了不会超出函数调用的项目。有关更多详细信息,我建议您再问一个顶级问题!
    猜你喜欢
    • 2012-05-19
    • 2016-08-05
    • 1970-01-01
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 1970-01-01
    相关资源
    最近更新 更多