如何 return 从特征实现中迭代 HashMap 的键?

How to return an iterator over the keys of a HashMap from a trait implementation?

我正在尝试用 Rust 构建一个简单的图形库。有一个特征 Graph 是任何图都必须实现的。此特征目前只有一个功能,nodes,它允许使用 for-in 循环迭代图的节点。

GraphMapGraph 的实现是围绕 HashMap 的轻量级包装器。 MapGraph 必须实现 Graph 特征方法 nodes。我在让它工作时遇到问题。

这是 Graph 的代码:

pub trait Graph<N> {
    fn nodes(&self) -> Box<dyn Iterator<Item = &N>>;
}

这里是 MapGraph 的代码:

use std::collections::HashMap;

use crate::rep::Graph;

pub struct MapGraph<N> {
    map: HashMap<N, HashMap<N, ()>>
}

impl<N> MapGraph<N> {
    pub fn new(map: HashMap<N, HashMap<N, ()>>) -> Self {
        MapGraph { map }
    }
}

impl<N> Graph<N> for MapGraph<N> {
    fn nodes(&self) -> Box<dyn Iterator<Item=&N>> {
        let keys = self.map.keys();

        Box::new(keys)
    }
}

编译器报错:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/lib.rs:19:29
   |
19 |         let keys = self.map.keys();
   |                             ^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 18:5...
  --> src/lib.rs:18:5
   |
18 | /     fn nodes(&self) -> Box<dyn Iterator<Item = &N>> {
19 | |         let keys = self.map.keys();
20 | |
21 | |         Box::new(keys)
22 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:19:20
   |
19 |         let keys = self.map.keys();
   |                    ^^^^^^^^
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected std::boxed::Box<(dyn std::iter::Iterator<Item = &N> + 'static)>
              found std::boxed::Box<dyn std::iter::Iterator<Item = &N>>

我发现了其他关于此错误的参考资料,但这些案例似乎与我这里的案例不同。

我正在使用 Box,因为 Graph 特征具有本身 returns 特征的功能。 What is the correct way to return an Iterator (or any other trait)? 将此方法作为一种选择,而我无法实施其他任何方法。如果有其他方法可以做到这一点。

解决这个具体问题我有哪些选择?

如果您明确指定要返回的特征对象 (dyn Iterator) 包含与 self.

的生命周期相关的引用,则它会起作用

如果不添加此绑定,编译器无法从函数签名中推断出在 self 移动或销毁后无法使用迭代器。由于编译器无法推断这一点,因此无法在函数的输出中安全地使用 self.map.keys()

添加了此绑定的工作示例:

pub trait Graph<N> {
    fn nodes<'a>(&'a self) -> Box<dyn Iterator<Item = &N> + 'a>;
}

use std::collections::HashMap;

pub struct MapGraph<N> {
    map: HashMap<N, HashMap<N, ()>>,
}

impl<N> MapGraph<N> {
    pub fn new(map: HashMap<N, HashMap<N, ()>>) -> Self {
        MapGraph { map }
    }
}

impl<N> Graph<N> for MapGraph<N> {
    fn nodes<'a>(&'a self) -> Box<dyn Iterator<Item = &N> + 'a> {
        let keys = self.map.keys();

        Box::new(keys)
    }
}

Playground

我原以为 Item = &'a N 的界限也是必需的,但我想这已经被“+ 'a”...

涵盖了

N.B。理解这样的错误:

expected std::boxed::Box<(dyn std::iter::Iterator<Item = &N> + 'static)>
   found std::boxed::Box<dyn std::iter::Iterator<Item = &N>>

您必须了解,出于人体工程学原因,编译器会自动向任何不合格的特征对象添加 + 'static 生命周期限定符。这意味着编译器将不合格的 Box<dyn MyTrait> 转换为 Box<(dyn MyTrait + 'static)>,这反过来意味着该对象不能包含 任何 引用,除了那些持续整个程序的生命周期。 考虑到这一点,您就会明白为什么 self.map.keys() 不符合这个严格的界限,并且需要更具体的显式界限。