问题
I'm trying to build a simple graph library in Rust. There is a trait Graph
that any graph must implement. This trait has only one function at the moment, nodes
, which allows iteration of the graph's nodes using a for-in loop.
An implementation of Graph
, MapGraph
, is a lightweight wrapper around a HashMap
. MapGraph
must implement the Graph
trait method nodes
. I'm having problems getting this to work.
Here's the code for Graph
:
pub trait Graph<N> {
fn nodes(&self) -> Box<dyn Iterator<Item = &N>>;
}
And here's the code for 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)
}
}
The compiler gives this error:
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>>
I've found other references to this error, but those cases don't seem to look like the one I have here.
I'm using Box
because the Graph
trait has a function that itself returns a trait. What is the correct way to return an Iterator (or any other trait)? gives this approach as one option, and I haven't been able to implement any of the the others. If there's another way to do this, that would be fine.
What are my options for resolving this specific problem?
回答1:
It works if you explicitly specify that the trait object (dyn Iterator
) that you are returning contains references that are tied to the lifetime of self
.
Without adding this bound, the compiler cannot infer from the function signature that the iterator cannot be used after self
is moved or destroyed. Because the compiler cannot infer this, it cannot safely use self.map.keys()
in the function's output.
Working example with this bound added:
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
I had thought that a bound of Item = &'a N
would also be required, but I guess that's already covered by the "+ 'a
"...
N.B. that to make sense of an error like:
expected std::boxed::Box<(dyn std::iter::Iterator<Item = &N> + 'static)>
found std::boxed::Box<dyn std::iter::Iterator<Item = &N>>
you have to understand that the compiler, for ergonomic reasons, automatically adds a + 'static
lifetime qualifier to any unqualified trait object. This means that an unqualified Box<dyn MyTrait>
is transformed by the compiler into a Box<(dyn MyTrait + 'static)>
, which in turn means that the object cannot contain any references except those that last for the lifetime of the entire program.
With this in mind you can see why self.map.keys()
does not fit this strict bound, and a more specific explicit bound is required.
来源:https://stackoverflow.com/questions/58611896/how-to-return-an-iterator-over-the-keys-of-a-hashmap-from-a-trait-implementation