【问题标题】:Trait object discrepancy between Vec and HashMapVec 和 HashMap 之间的特征对象差异
【发布时间】:2020-10-02 21:49:32
【问题描述】:

我一直在努力理解为什么以下代码的行为方式 (Playground):

use std::collections::HashMap;

trait Trait<'a> {
    fn get_enum(&'a self) -> Enum<'a>;
}

#[derive(Clone)]
enum Enum<'a> {
    Arr(Vec<&'a dyn Trait<'a>>),
    Map(HashMap<String, &'a dyn Trait<'a>>)
}

impl<'a> Trait<'a> for Enum<'a> {
    fn get_enum(&'a self) -> Enum<'a> {
        self.clone()
    }
}

fn process<'a>(val: &'a dyn Trait<'a>) -> Vec<&'a dyn Trait<'a>> {
    let mut traits: Vec<&dyn Trait> = vec![];
    match val.get_enum() {
        Enum::Arr(v) => {
            for elem in v {
                traits.push(elem);
            }
        },
        Enum::Map(m) => {
            for elem in m.values() {
                traits.push(elem);
            }
        }
    }
    traits
}

这会引发错误:

error[E0277]: the trait bound `&dyn Trait<'_>: Trait<'_>` is not satisfied
  --> src/main.rs:29:29
   |
29 |                 traits.push(elem);
   |                             ^^^^ the trait `Trait<'_>` is not implemented for `&dyn Trait<'_>`
   |
   = note: required for the cast to the object type `dyn Trait<'_>`

对我来说奇怪的不是错误,而是错误只显示来自HashMap 而不是来自Vec 的迭代器的列表值。谁能给我解释一下:

  1. 为什么这两个结构的迭代器行为如此不同
  2. 如何将值从我的地图传递到我的数组中

我发现通过get 调用检索任何值时也会出现同样的现象。

【问题讨论】:

  • for elem in m.values() 通过引用获取m 并迭代引用。 for elem in v 按值获取 v 并迭代值。如果你写for elem in &amp;v都失败,如果你写for (_, elem) in m都通过。
  • @trentcl 让这个答案怎么样?
  • 我主要是想找到某种调用get 的方法来推入向量。我也理解迭代器的区别,但很难理解它们对 trait 对象的影响。

标签: rust traits trait-objects


【解决方案1】:

区别不在于VecHashMap,而在于你如何迭代它们。 for 循环在内部使用IntoIterator(参见Why is iterating over a collection via `for` loop considered a "move" in Rust?),因此elem 的类型取决于可迭代对象。

for elem in v {
    traits.push(elem);
}

Vec&lt;T&gt; 实现了IntoIterator&lt;Item = T&gt;,所以循环内部elem&amp;'a dyn Trait&lt;'a&gt;

for elem in m.values() {
    traits.push(elem);
}

HashMap&lt;_, T&gt;::values 借用self 来创建Iterator&lt;Item = &amp;T&gt;(实现IntoIterator&lt;Item = &amp;T&gt;)。由于m 的值类型已经是一个引用,所以在循环内elem 类似于&amp;'b &amp;'a dyn Trait&lt;'a&gt;(其中'bm 的借用的生命周期)。

您收到错误the trait bound `&amp;dyn Trait&lt;'_&gt;: Trait&lt;'_&gt;` is not satisfied 的原因是因为编译器试图强制 &amp;'b &amp;'a dyn Trait&amp;'b dyn Trait,但它不能因为&amp;'a dyn Trait 没有实现Trait .但即使这样做也不能解决问题——当您尝试从函数返回生命周期 'b 的引用时,您只会得到一个借用错误。

如何将地图中的值传递到数组中?

我在 cmets 中提到您可以按值迭代 m,这类似于您对 v 所做的事情:

for (_key, elem) in m {
    traits.push(elem);
}

这是破坏性的迭代,因为之后您不能使用m。另一种选择是,因为共享引用实现了Copy,所以在您遍历它时将它们复制到地图之外:

for elem in m.values() {
    traits.push(*elem);          // using * to dereference
}

for &elem in m.values() {        // using & to destructure
    traits.push(elem);
}

这些循环做同样的事情;唯一的区别是elem 的类型。

【讨论】:

    猜你喜欢
    • 2019-04-16
    • 2020-09-18
    • 2013-12-06
    • 2020-02-29
    • 1970-01-01
    • 2019-07-07
    • 1970-01-01
    • 2017-11-23
    • 1970-01-01
    相关资源
    最近更新 更多