【问题标题】:Storing a nested trait instance in a generic variable将嵌套特征实例存储在泛型变量中
【发布时间】:2020-08-21 18:06:07
【问题描述】:

如果我有一个通用泛型类型的“外部”特征(Collection,下面),那么我可以创建一个具有“内部”特征类型(Collection<&dyn Stringable>)的实例。然后我可以在该实例中使用任何值,只要这些值实现嵌套特征 (Stringable):

fn main() {
    let mut vs: &mut Collection<&dyn Stringable> = &mut vec![];
    vs.add(&1);
    vs.add(&true);
    vs.add(&3);
    for v in vs.get_all() {
        println!("{}", v.string());
    }
}

trait Collection<T> {
    fn add(&mut self, v: T);
    fn get_all(&self) -> &Vec<T>;
}

impl<T> Collection<T> for Vec<T> {
    fn add(&mut self, v: T) {
        self.push(v)
    }
    fn get_all(&self) -> &Vec<T> {
        &self
    }
}

trait Stringable {
    fn string(&self) -> String;
}

impl Stringable for i8 {
    fn string(&self) -> String {
        format!("int({})", self)
    }
}

impl Stringable for bool {
    fn string(&self) -> String {
        format!("bool({})", self)
    }
}

但是,如果我同时实现了一个类型 (Collection&lt;i8&gt;) 的外部和内部特征,则该类型的值不能放入 Collection&lt;&amp;dyn Stringable&gt; 变量中,即使第一个类型的内部类型 (@ 987654328@) 实现了第二个 (Stringable)。下面的代码给出了以下错误:

the trait `Collection<&dyn Stringable>` is not implemented for `StaticCollection`

代码:

fn main() {
    let mut vs: &mut Collection<&dyn Stringable> = &mut StaticCollection{};
    for v in vs.get_all() {
        println!("{}", v.string());
    }
}

struct StaticCollection {}

impl Collection<i8> for StaticCollection {
    fn add(&mut self, v: i8) {}
    fn get_all(&self) -> &Vec<i8> {
        &vec![1, 2, 3]
    }
}

是否有可能,例如,写impl Collection&lt;&amp;dyn Stringable&gt; for StaticCollection 之类的东西,以便StaticCollection 可以存储在通用Collection&lt;&amp;dyn Stringable&gt; 变量中?

【问题讨论】:

  • &amp;vec![1, 2, 3] 部分无法编译,因为它试图返回对临时对象的引用。我假设你可以解决这个问题,但你会遇到类似的问题,因为 Vec&lt;T&gt; is not interchangeable with Vec&lt;dyn U&gt; 绑定返回 Vec&lt;&amp;dyn Stringable&gt;,你必须创建一个新的 Vec 以将其作为特征对象返回。
  • 对于StaticCollection 实现Collection&lt;i8&gt; Collection&lt;&amp;dyn Stringable&gt; 没有一般 问题,只是您选择的接口可能会导致问题。

标签: generics types rust traits


【解决方案1】:

这取决于StaticCollection是如何实现的,但在技术上是可行的:

impl<'a> Collection<&'a dyn Stringable> for StaticCollection {
    fn add(&mut self, v: &'a dyn Stringable) {
        
    }
    fn get_all(&self) -> &Vec<&'a dyn Stringable> { 
        panic!()
    }
}

但是StaticCollection 必须使用&amp;dyn Stringable 项目创建。 例如(Link to playground):

fn main() {
    let mut vs: &mut Collection<&dyn Stringable> = &mut StaticCollection { 
        v: vec![&0,&1,&2]
    };
    for v in vs.get_all() {
        println!("{}", v.string());
    }
}

struct StaticCollection {
    v: Vec<&'static dyn Stringable>,
}

impl<'a> Collection<&'a dyn Stringable> for StaticCollection {
    fn add(&mut self, v: &'a dyn Stringable) {
        
    }
    fn get_all(&self) -> &Vec<&'a dyn Stringable> { 
        &self.v
    }
}

请注意,我们在'aitems 生命周期内是通用的,但因为(定义的)特征需要get_all 返回一个@ 987654331@ 借自self,无法“转换”Vec。所以如果你定义了StaticCollection { v: Vec&lt;&amp;i8&gt; }(或者Vec&lt;i8&gt;get_all就不能实现(没有“泄漏”一个新的Vec,这可能不是你想要的)。

get_all 的完整生命周期实际上是:

fn get_all<'s>(&'s self) -> &'s Vec<&'a ...>

一句警告:dyn Trait 会让您在编译器和借用检查器方面陷入困境。 您可能还想查看 this blog post 关于探索类似概念的“类型族”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-18
    • 2019-07-14
    • 1970-01-01
    • 2014-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多