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

时间:2015-04-11 16:34:57

标签: rust lifetime

我在尝试理解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 个答案:

答案 0 :(得分: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();