【问题标题】:How can I transform an iterator trait object of concrete types into an iterator trait object of trait objects?如何将具体类型的迭代器特征对象转换为特征对象的迭代器特征对象?
【发布时间】:2019-10-23 01:30:32
【问题描述】:

我有一个 trait,它包含一个函数来返回一个迭代器,而不是对另一个 trait 的引用,例如:

pub trait ParentInterface {
    fn children<'a>(&'a self) -> Box<dyn Iterator<Item = &'a ChildInterface>>;
}

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 ChildInterface>> {
        Box::new(self.my_children.iter()) // Compiler error!
    }
}

impl ChildInterface for ConcreteChild {
    fn some_method(&self) -> bool {
        self.my_value
    }
}

上面的例子产生了 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>`

我假设 my_children.iter() 返回一个带有错误 Item 类型(具体类型而不是 trait 类型)的迭代器 - 如何解决这个问题?

【问题讨论】:

    标签: reference rust iterator traits


    【解决方案1】:

    默认情况下,特征对象以'static 为界。必须指定生命周期'a,然后才能正确映射迭代器(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
        }
    }
    

    注意变化:

    • 迭代器的引用边界是'a,就像项目一样:

      dyn Iterator</*...*/> + 'a
      
    • 每个具体类型都映射到一个 trait 对象:

      .map(|c| c as &'a dyn ChildInterface)
      

      请注意,您可以简化符号以使推理起作用:.map(|c| c as _)

    您可以使用生命周期'_ 进一步简化:

    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 _))
        }
    }
    

    【讨论】:

    • 谢谢!您能否详细说明或参考文档说明为什么 Iterator 返回类型需要+ 'a?为什么默认是'static
    • @Cybran 我已经添加了来源,但没有解释“为什么”
    猜你喜欢
    • 1970-01-01
    • 2020-02-10
    • 2016-08-27
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 1970-01-01
    • 2015-05-26
    相关资源
    最近更新 更多