为什么 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,并且对 Self
有 Sized
约束,因此不是对象安全的。
但是,如果我注释掉 ...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_each
和 Observable::subscribe
的函数签名几乎相同。
什么给了?
Iterator::for_each
的 similar 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: Sized
但 dyn 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), |_| {});
}
发出:
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
.
我在研究一些 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,并且对 Self
有 Sized
约束,因此不是对象安全的。
但是,如果我注释掉 ...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_each
和 Observable::subscribe
的函数签名几乎相同。
什么给了?
Iterator::for_each
的 similar 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: Sized
但 dyn 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), |_| {});
}
发出:
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
.