为什么 Rust 允许您在特征对象上调用“Iterator::for_each()”?

Why does Rust allow you to call `Iterator::for_each()` on a trait object?

我在研究一些 API 概念并注意到 Rust 的 Iterator trait 中的一些特殊之处。

我有以下特征定义:

trait Observable {
    type Item;

    fn subscribe<F>(self, f: F) -> bool
    where
        Self: Sized,
        F: FnMut(Self::Item) + 'static;
}

然后我开始写下面的测试:

#[test]
#[should_panic]
fn trait_obj() {
    let mut v: Vec<Box<dyn Iterator<Item = ()>>> = vec![];
    let mut v2: Vec<Box<dyn Observable<Item = Ref<u8>>>> = vec![];

    v.remove(0).for_each(|_| {});
    v2.remove(0).subscribe(|_| {});
}

上面的测试没有像预期的那样编译; subscribe() 按值获取 self,并且对 SelfSized 约束,因此不是对象安全的。 但是,如果我注释掉 ...subscribe 行,它会编译!

对我来说奇怪的是,Iterator::for_each() 有相同的限制。为什么允许 Iterator 而不允许 Observable?它是启用此功能的实验性功能吗?

这里是Iterator::for_each的函数签名供参考:

// Iterator::for_each
fn for_each<F>(self, f: F)
where
    Self: Sized,
    F: FnMut(Self::Item);

Iterator::for_eachObservable::subscribe 的函数签名几乎相同。

什么给了?

Iterator::for_eachsimilar question was asked over on the Rust forum. To summarize, the full signature

fn for_each<F>(self, f: F)
    where
        Self: Sized,
        F: FnMut(Self::Item);

关键是 Self: Sized 部分。这意味着“只有 Self 的大小已知时才能调用此函数”,因此 for_each 明确禁止用于 dyn Iterator 类型的特征对象。 Iterator 上的几个方法都是如此,并且是 Rust 两全其美的方式:一个仍然可以用作对象但在静态使用时具有额外功能的特征。

我不确定为什么 Self: Sized 在 Rustdoc 页面中被隐藏了;您实际上必须单击“查看源代码”才能看到它,这对我来说似乎是一个疏忽。

你的错误是你认为你调用了 <dyn Iterator<Item = ()>>::for_each(),然后你想知道如果 for_each() 需要 Self: Sizeddyn Iterator<Item = ()> 显然不是 Sized。但你错了。你可以看到,如果你将使用 UFCS(通用函数调用语法):

#[test]
#[should_panic]
fn trait_obj() {
    let mut v: Vec<Box<dyn Iterator<Item = ()>>> = vec![];

    // v.remove(0).for_each(|_| {});
    <dyn Iterator<Item = ()>>::for_each(v.remove(0), |_| {});
}

Playground.

发出:

error[E0277]: the size for values of type `dyn Iterator<Item = ()>` cannot be known at compilation time
   --> src/lib.rs:7:5
    |
7   |     <dyn Iterator<Item = ()>>::for_each(v.remove(0), |_| {});
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `dyn Iterator<Item = ()>`
note: required by a bound in `for_each`

error[E0308]: mismatched types
 --> src/lib.rs:7:41
  |
7 |     <dyn Iterator<Item = ()>>::for_each(v.remove(0), |_| {});
  |                                         ^^^^^^^^^^^ expected trait object `dyn Iterator`, found struct `Box`
  |
  = note: expected trait object `dyn Iterator<Item = ()>`
                   found struct `Box<dyn Iterator<Item = ()>>`
help: consider unboxing the value
  |
7 |     <dyn Iterator<Item = ()>>::for_each(*v.remove(0), |_| {});
  |                                         +

error[E0277]: the size for values of type `dyn Iterator<Item = ()>` cannot be known at compilation time
 --> src/lib.rs:7:41
  |
7 |     <dyn Iterator<Item = ()>>::for_each(v.remove(0), |_| {});
  |                                         ^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `Sized` is not implemented for `dyn Iterator<Item = ()>`
  = note: all function arguments must have a statically known size

而且这个错误也提示你为什么以前的版本有效:你没有调用 <dyn Iterator<Item = ()>>::for_each(),你调用了 Box::<dyn Iterator<Item = ()>>::for_each()Box<Iterator> implements Iterator itself,所以它起作用了。您可以在 MIR 中明确看到:

v.remove(0).for_each(|_| {});
// Snip
_2 = <Box<dyn Iterator<Item = ()>> as Iterator>::for_each::<[closure@src/lib.rs:4:26: 4:32]>(move _3, move _5) -> [return: bb3, unwind: bb5];
// Snip

Playground(从菜单中选择“显示 MIR”)。

如果您已经为 Box<O> where O: Observable 实施了 Observable,它也适用于您...

...除非你不能。因为您无法将呼叫转接至 for_each()。它使用 Iterator 的原因是它不转发此呼叫,而是使用 the default implementation that calls next() again and again。因为 next() 需要 &mut self,所以不需要 Self: Sized.