简体   繁体   中英

How can I transform an iterator trait object of concrete types into an iterator trait object of trait objects?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM