如何防止移入功能的 impl Trait 在借用时被删除?

How do I prevent impl Trait moved into function from being dropped while still borrowed?

我想定义一个 trait Holder 可以通过 items 方法迭代一些。我不想从这个方法 return 一个特征对象,因为如果可能的话我想坚持使用静态分派和堆栈分配。我的系统工作正常,但在某种令人惊讶的情况下失败了。

代码如下:

pub trait Holder<'a, N: 'a> {
    type Items: Iterator<Item=&'a N>;

    fn items(&'a self) -> Self::Items;
}

struct Impl<N> {
    items: Vec<N>
}

impl<'a, N: 'a> Holder<'a, N> for Impl<N> {
    type Items = std::slice::Iter<'a, N>;

    fn items(&'a self) -> Self::Items {
        self.items.iter()
    }
}

fn use_holder<'a, N: 'a>(holder: impl Holder<'a, N>) {
    // COMPILE ERROR
    for item in holder.items() {

    }
}

这是错误:

error[E0309]: the parameter type `impl Holder<'a, N>` may not live long enough
  --> src/graph/test.rs:20:17
   |
19 | fn use_holder<'a, N: 'a>(holder: impl Holder<'a, N>) {
   |                                  ------------------ help: consider adding an explicit lifetime bound...: `impl Holder<'a, N> + 'a`
20 |     for item in holder.items() {
   |                 ^^^^^^
   |
note: ...so that the type `impl Holder<'a, N>` is not borrowed for too long
  --> src/graph/test.rs:20:17
   |
20 |     for item in holder.items() {
   |                 ^^^^^^

error[E0309]: the parameter type `impl Holder<'a, N>` may not live long enough
  --> src/graph/test.rs:20:24
   |
19 | fn use_holder<'a, N: 'a>(holder: impl Holder<'a, N>) {
   |                                  ------------------ help: consider adding an explicit lifetime bound...: `impl Holder<'a, N> + 'a`
20 |     for item in holder.items() {
   |                        ^^^^^
   |
note: ...so that the reference type `&'a impl Holder<'a, N>` does not outlive the data it points at
  --> src/graph/test.rs:20:24
   |
20 |     for item in holder.items() {
   |                        ^^^^^

error: aborting due to 2 previous errors; 4 warnings emitted

For more information about this error, try `rustc --explain E0309`.

我听从了编译器的建议并添加了显式生命周期:

pub trait Holder<'a, N: 'a> {
    type Items: Iterator<Item=&'a N>;

    fn items(&'a self) -> Self::Items;
}

struct Impl<N> {
    items: Vec<N>
}

impl<'a, N: 'a> Holder<'a, N> for Impl<N> {
    type Items = std::slice::Iter<'a, N>;

    fn items(&'a self) -> Self::Items {
        self.items.iter()
    }
}

fn use_holder<'a, N: 'a>(holder: impl Holder<'a, N> + 'a) {
    for item in holder.items() {

    }
}

给出错误:

error[E0597]: `holder` does not live long enough
  --> src/graph/test.rs:20:17
   |
19 | fn use_holder<'a, N: 'a>(holder: impl Holder<'a, N> + 'a) {
   |               -- lifetime `'a` defined here
20 |     for item in holder.items() {
   |                 ^^^^^^--------
   |                 |
   |                 borrowed value does not live long enough
   |                 argument requires that `holder` is borrowed for `'a`
...
23 | }
   | - `holder` dropped here while still borrowed

我已经看到 this question 和其他几个与此问题无关的问题,但无法理解如何继续前进。

除了 return 从 Hoder#items 获取特征对象外,我还有哪些选择可以使 use_hodler 中的循环正常工作?

这实际上非常困难 - 生命周期和关联类型似乎是 Rust 中那些(尚未)得到很好支持的奇怪极端情况之一。如果我说我正确理解了错误的原因,那我就是在撒谎,但是我已经设法让这个工作 对于这个特定的例子 使用 Higher Ranked Trait Bounds.

您只需更改一行:

fn use_holder<N>(holder: impl for<'a> Holder<'a, N>) {

请注意,生命周期参数已移至 for<'a>。这意味着 holder 参数为每​​个可能的生命周期 'a 实现 Holder<'a, N>,而不是为至少与函数一样长的特定 'a 实现。

你需要使用 for<'a> ... 大多数你使用这个特性的地方,所以它很快就会变得混乱。并且更高等级的特征边界不是一个常用的特性:它非常小众并且在最常见的情况下被忽略了,所以它不会帮助提高易读性或可维护性。

我建议您更改架构,尽可能避免这种构造。