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

flo*_*ero 3 casting reference traits rust

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

use std::fmt::Display;

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

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

let items: Vec<u32> = (1..10).into_iter().collect();
show_items(items.iter());
Run Code Online (Sandbox Code Playgroud)

我收到错误:

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>) {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

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

显式强制转换效果很好:

show_items(items.iter().map(|item| item as &Display));
Run Code Online (Sandbox Code Playgroud)

它也适用于单个项目:

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

Sve*_*ach 7

T从类型到dyn Trait实现的Trait隐式转换T是所谓的unsized 强制转换,一种特殊类型的强制转换。虽然 Rust 不太愿意进行隐式类型转换,但强制转换确实会在强制转换站点隐式发生,但不会在其他地方发生。

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

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

你的定义show_items()

fn show_items<'a>(items: impl Iterator<Item = &'a Display>)
Run Code Online (Sandbox Code Playgroud)

另一方面则是完全不同的故事。这里使用的语法impl是以下的简写

fn show_items<'a, I>(items: I)
where
    I: Iterator<Item = &'a dyn Display>,
Run Code Online (Sandbox Code Playgroud)

该函数对于迭代器类型是通用的,并且编译器会验证您实际传入的类型是否实现了特征绑定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,
Run Code Online (Sandbox Code Playgroud)

(我也改为IteratorIntoIterator因为这样更通用、更方便。)