I have a trait which contains a function to return an iterator over references to another trait, like:
pub trait ParentInterface {
fn children<'a>(&'a self) -> Box<dyn Iterator<Item = &'a ChildInterface>>;
}
pub trait ChildInterface {
fn some_method(&self) -> bool;
}
How can an iterator of the correct type be returned when implementing this trait for a concrete type which stores a vector of concrete values?
pub struct ConcreteParent {
my_children: Vec<ConcreteChild>,
}
pub struct ConcreteChild {
my_value: bool,
}
impl ParentInterface for ConcreteParent {
fn children<'a>(&'a self) -> Box<dyn Iterator<Item = &'a ChildInterface>> {
Box::new(self.my_children.iter()) // Compiler error!
}
}
impl ChildInterface for ConcreteChild {
fn some_method(&self) -> bool {
self.my_value
}
}
The above example yields a compiler error for Rust 2018:
error[E0271]: type mismatch resolving `<std::slice::Iter<'_, ConcreteChild> as std::iter::Iterator>::Item == &dyn ChildInterface`
--> src/lib.rs:19:9
|
19 | Box::new(self.my_children.iter()) // Compiler error!
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `ConcreteChild`, found trait ChildInterface
|
= note: expected type `&ConcreteChild`
found type `&dyn ChildInterface`
= note: required for the cast to the object type `dyn std::iter::Iterator<Item = &dyn ChildInterface>`
I assume that my_children.iter()
returns an iterator with the wrong Item
type (the concrete type instead of the trait type) - how can this be solved?
By default, a trait object is bounded by 'static
. You must specify the lifetime 'a
and then you can correctly map the iterator ( source ):
pub trait ParentInterface {
fn children<'a>(&'a self) -> Box<dyn Iterator<Item = &'a dyn ChildInterface> + 'a>;
}
pub trait ChildInterface {
fn some_method(&self) -> bool;
}
pub struct ConcreteParent {
my_children: Vec<ConcreteChild>,
}
pub struct ConcreteChild {
my_value: bool,
}
impl ParentInterface for ConcreteParent {
fn children<'a>(&'a self) -> Box<dyn Iterator<Item = &'a dyn ChildInterface> + 'a> {
Box::new(self.my_children.iter().map(|c| c as &'a dyn ChildInterface))
}
}
impl ChildInterface for ConcreteChild {
fn some_method(&self) -> bool {
self.my_value
}
}
Note the changes:
The reference bounds of the iterator are 'a
, just like the items:
dyn Iterator</*...*/> + 'a
Each concrete type is mapped to a trait object:
.map(|c| c as &'a dyn ChildInterface)
Note that you can simplify the notation to let the inference work: .map(|c| c as _)
You can simplify further by using the lifetime '_
:
pub trait ParentInterface {
fn children(&self) -> Box<dyn Iterator<Item = &dyn ChildInterface> + '_>;
}
// ...
impl ParentInterface for ConcreteParent {
fn children(&self) -> Box<dyn Iterator<Item = &dyn ChildInterface> + '_> {
Box::new(self.my_children.iter().map(|c| c as _))
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.