如何从特征实现中返回HashMap的键上的迭代器?

时间:2019-10-29 16:43:03

标签: rust graph-theory

我正在尝试在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特质具有一个本身返回特质的函数。 What is the correct way to return an Iterator (or any other trait)?将此方法作为一种选择,但我无法实现其他任何一种。如果还有另一种方法可以,那就没问题了。

解决此特定问题有哪些选择?

1 个答案:

答案 0 :(得分:2)

如果您明确指定要返回的特征对象(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”中了...


可以理解这样的错误:

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

您必须了解,出于人体工程学的原因,编译器会自动将+ 'static生存期限定符添加到任何不合格的trait对象。这意味着编译器会将不合格的Box<dyn MyTrait>转换为Box<(dyn MyTrait + 'static)>,这反过来意味着该对象不能包含 any 引用,除非这些引用在整个生命周期中都有效。整个程序。 考虑到这一点,您可以了解为什么self.map.keys()不符合此严格限制,并且需要更具体的显式限制。