为什么引用的迭代器项不转换为特征对象引用?

时间:2019-02-04 16:13:43

标签: casting reference rust traits

我正在尝试定义一个应该接收迭代器的函数,其中每个项目都是对特征对象的引用。例如:

use std::fmt::Display;

fn show_items<'a>(items: impl Iterator<Item = &'a Display>) {
    items.for_each(|item| println!("{}", item));
}

当我尝试在迭代器上调用它时,其中每个项目都是对实现Display的类型的引用:

let items: Vec<u32> = (1..10).into_iter().collect();
show_items(items.iter());

我得到一个错误:

error[E0271]: type mismatch resolving `<std::slice::Iter<'_, u32> as std::iter::Iterator>::Item == &dyn std::fmt::Display`
 --> src/lib.rs:9:5
  |
9 |     show_items(items.iter());
  |     ^^^^^^^^^^ expected u32, found trait std::fmt::Display
  |
  = note: expected type `&u32`
             found type `&dyn std::fmt::Display`
note: required by `show_items`
 --> src/lib.rs:3:1
  |
3 | fn show_items<'a>(items: impl Iterator<Item = &'a Display>) {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

为什么&u32不被认为是&dyn std::fmt::Display

显式强制转换效果很好

show_items(items.iter().map(|item| item as &Display));

它也可以用于单个项目:

fn show_item(item: &Display) {
    println!("{:?}", item);
}
let item: u32 = 1;
show_item(&item);

1 个答案:

答案 0 :(得分:3)

T实现的dyn Trait从类型TraitT的隐式转换是所谓的unsized coercion,是一种特殊的强制。尽管Rust不太愿意使用隐式类型转换,但强制确实会在coercion sites上隐式发生,而在其他地方则不会。

函数调用参数是强制站点。这就解释了为什么您的show_item()函数可以按预期工作。

还可以使用as运算符显式执行所有强制。因此,使用map()的版本可以正常工作。

您对show_items()的定义,

fn show_items<'a>(items: impl Iterator<Item = &'a Display>)
另一方面

则完全不同。这里使用的impl语法是

的简写
fn show_items<'a, I>(items: I)
where
    I: Iterator<Item = &'a dyn Display>,

该函数在迭代器类型上是 generic ,并且编译器验证您实际传入的类型是否实现了特征绑定Iterator<Item = &'a dyn Display>。示例代码中的类型std::slice::Iter<'_, u32>根本没有,因此会出现错误。没有强制将参数转换为其他类型以使其实现通用函数所需的某些特征绑定的强制转换。还完全不清楚将std::slice::Iter<'_, u32>转换为哪种类型以将其转换为&dyn Display上的迭代器。

请注意,您的函数定义版本由于要求对特征对象进行迭代而不必要地受到了限制。仅要求迭代器项改为实现Display会更自然,更高效:

fn show_items<I>(items: I)
where
    I: IntoIterator,
    I::Item: Display,

(我也将Iterator更改为IntoIterator,因为这更通用,更方便。)