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

Jos*_*ada 6 rust trait-objects

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

我有以下特征定义:

trait Observable {
    type Item;

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

然后我继续编写以下测试:

#[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(|_| {});
}
Run Code Online (Sandbox Code Playgroud)

正如人们所期望的那样,上述测试无法编译;subscribe()按值获取 self,并且对 具有Sized约束Self,因此不是对象安全的。但是,如果我注释掉该...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);
Run Code Online (Sandbox Code Playgroud)

Iterator::for_each和的函数签名Observable::subscribe几乎相同。

是什么赋予了?

Cha*_*man 6

你的错误在于你认为你打电话了<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), |_| {});
}
Run Code Online (Sandbox Code Playgroud)

游乐场

发出:

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

这个错误还提示您为什么以前的版本有效:您没有调用<dyn Iterator<Item = ()>>::for_each(),您调用了Box::<dyn Iterator<Item = ()>>::for_each()Box<Iterator>实现了Iterator它自己,所以它起作用了。您可以在 MIR 中明确地看到这一点:

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

游乐场(从菜单中选择“显示 MIR”)。

如果您已经实施过ObservableBox<O> where O: Observable它也会对您有用......

...除非你不能。因为您无法将呼叫转接至for_each()。它使用的原因Iterator是它不转发此调用,而是使用一次又一次调用的默认实现next()。因为next()需要&mut self,所以不需要Self: Sized