首页 > 解决方案 > 如何从特征实现中返回 HashMap 键的迭代器?

问题描述

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

Graph, ,的实现MapGraph是一个轻量级的包装器HashMapMapGraph必须实现Graphtrait 方法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,因为Graphtrait 有一个本身返回 trait 的函数。返回迭代器(或任何其他特征)的正确方法是什么?将这种方法作为一种选择,而我无法实现其他任何一种方法。如果有其他方法可以做到这一点,那很好。

我有哪些解决这个特定问题的选择?

标签: rustgraph-theory

解决方案


如果您明确指定要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)
    }
}

操场

我原以为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生命周期限定符添加到任何未限定的特征对象。这意味着Box<dyn MyTrait>编译器将 unqualified 转换为 a Box<(dyn MyTrait + 'static)>,这反过来意味着对象不能包含任何引用,除了那些持续整个程序生命周期的引用。考虑到这一点,您可以看到为什么self.map.keys()不符合这个严格的界限,并且需要更具体的明确界限。


推荐阅读