Vec 和 HashMap 之间的特征对象差异

Trait object discrepancy between Vec and HashMap

我一直在努力理解为什么以下代码会这样运行 (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 调用检索任何值时。

区别不在于 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<T> 实现了 IntoIterator<Item = T>,所以在循环 elem 内部是 &'a dyn Trait<'a>

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

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

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

How I can pass values from my map into my array?

我在评论中提到您可以按值迭代 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.

的类型