rust 与返回迭代器的生存期匹配的Trait对象生存期触发生存期错误

5ktev3wc  于 2023-01-17  发布在  其他
关注(0)|答案(1)|浏览(172)

我有一个trait,它有一个返回Box<dyn Iterator<...>>的函数,根据我的理解,根据定义,这个返回的迭代器的生命周期需要匹配trait对象的生命周期,但是在一个函数中,如果在末尾同时删除了这两个参数,我会得到一个生命周期错误,这里是一个最小的例子,并添加了一些假设作为注解:

trait Foo<'a> {
    // The returned iterator's lifetime should match self's
    fn bar(&'a self) -> Box<dyn Iterator<Item = usize> + 'a>;
}

struct FooImpl {
    values: Vec<usize>
}

impl<'a> Foo<'a> for FooImpl {
    fn bar(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> {
        Box::new(self.values.iter().cloned())
    }
}

fn foo_bar_caller<'a>(foo: Box<dyn Foo<'a>>) {
    // `foo` is moved into this fn
    let _iter = foo.bar();
    // `_iter` and `foo` should drop
}

fn main() {
    let foo = FooImpl { values: vec![1, 2, 3, 4, 5] };
    foo_bar_caller(Box::new(foo));
}

(代码为rust playground。)运行此命令会出现以下生存期错误:

error[E0597]: `*foo` does not live long enough
  --> src/main.rs:17:17
   |
16 | fn foo_bar_caller<'a>(foo: Box<dyn Foo<'a>>) {
   |                   -- lifetime `'a` defined here
17 |     let _iter = foo.bar();
   |                 ^^^^^^^^^
   |                 |
   |                 borrowed value does not live long enough
   |                 argument requires that `*foo` is borrowed for `'a`
18 |     // `_iter` and `foo` should drop
19 | }
   | - `*foo` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` due to previous error
eanckbw9

eanckbw91#

你可能想让 functionFoo::bar在引用生存期内成为泛型,而不是trait Foo

trait Foo {
    // The returned iterator's lifetime should match self's
    fn bar<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a>;
}

通过将生存期放在trait Foo上,您不必(至少在本上下文中)限制可以用于特定Foo::<'_>::bar实现的引用的生存期。(在X1 M6 N1 X是X1 M7 N1 X的上下文中的X1 M5 N1 X)调用只能采用具有生存期X1 M8 N1 X的引用,但正如@MaximilianBurszley在评论中指出的,您实际传递给它的是一个临时借用,它 * 不 * 具有相同的生存期'a
Playground
注意,如果你让函数接受一个具有相同生存期的引用,你可以让foo_bar_callerwork with your existing codetraitFoo在生存期内是泛型的):

fn foo_bar_caller<'a>(foo: &'a dyn Foo<'a>) {
    // `foo` is moved into this fn
    let _iter = foo.bar();
    // `_iter` and `foo` should drop
}

fn main() {
    let foo = FooImpl { values: vec![1, 2, 3, 4, 5] };
    foo_bar_caller(&foo);
}

或者,您可以使用higher-ranked trait bound(HRTB)来指定foo_bar_caller必须接受一个值,该值在'b的 * 所有 * 生存期内实现Foo<'b>

fn foo_bar_caller(foo: Box<dyn for<'b> Foo<'b>>) {
    // `foo` is moved into this fn
    let _iter = foo.bar();
    // `_iter` and `foo` should drop
}

这允许值fooFoo<'_>实现在foo.bar()调用期间创建的临时借用的生存期内有效(因为它在 * 所有 * 生存期内实现Foo<'_>)。
Playground for HRTB version

相关问题