首先,让我们对类型进行注释:
fn sort_and_print(v: &Vec<Box<dyn Foo>>) {
let mut v_copy: Vec<&Box<dyn Foo>> = Vec::new();
for val in v {
v_copy.push(val);
}
v_copy.sort_by_key(|o: &&Box<dyn Foo>| <dyn Foo>::value(&***o));
for val in v_copy {
println!("{}", <dyn Foo>::value(&**val));
}
}
遍历
&Vec<T>
会产生一个迭代器,该迭代器遍历
&T
(与
.iter()
方法相同)。
现在我们可以看到,我们可以通过在
v
上调用
.into_iter()
,然后调用
.collect()
将其转换为迭代器(这就是
for
循环所做的),或者将
into_iter()
替换为
iter()
(由于我们正在遍历引用,因此更符合惯例)。
fn sort_and_print(v: &Vec<Box<dyn Foo>>) {
let mut v_copy: Vec<&Box<dyn Foo>> = v.iter().collect();
v_copy.sort_by_key(|o| o.value());
for val in v_copy {
println!("{}", val.value());
}
}
然而,我们仍然只有参考的副本(
&Box<dyn Foo>
)。为什么我们不能克隆向量呢?
如果我们尝试...
fn sort_and_print(v: &Vec<Box<dyn Foo>>) {
let mut v_copy = v.clone();
v_copy.sort_by_key(|o| o.value());
for val in v_copy {
println!("{}", val.value());
}
}
"...编译器对我们喊道:"
warning: variable does not need to be mutable
--> src/main.rs:45:9
|
45 | let mut v_copy = v.clone();
| ----^^^^^^
| |
| help: remove this `mut`
|
= note: `#[warn(unused_mut)]` on by default
error[E0596]: cannot borrow `*v_copy` as mutable, as it is behind a `&` reference
--> src/main.rs:46:5
|
45 | let mut v_copy = v.clone();
| ---------- help: consider changing this to be a mutable reference: `&mut Vec<Box<dyn Foo>>`
46 | v_copy.sort_by_key(|o| o.value());
| ^^^^^^ `v_copy` is a `&` reference, so the data it refers to cannot be borrowed as mutable
“什么????”
“好吧,让我们尝试指定类型。这样可以使编译器更加智能。”
fn sort_and_print(v: &Vec<Box<dyn Foo>>) {
let mut v_copy: Vec<Box<dyn Foo>> = v.clone();
v_copy.sort_by_key(|o| o.value());
for val in v_copy {
println!("{}", val.value());
}
}
“不是。”
error[E0308]: mismatched types
--> src/main.rs:45:41
|
45 | let mut v_copy: Vec<Box<dyn Foo>> = v.clone();
| ----------------- ^^^^^^^^^
| | |
| | expected struct `Vec`, found reference
| | help: try using a conversion method: `v.to_vec()`
| expected due to this
|
= note: expected struct `Vec<Box<dyn Foo>>`
found reference `&Vec<Box<dyn Foo>>`
好的,让我们使用编译器的建议:
fn sort_and_print(v: &Vec<Box<dyn Foo>>) {
let mut v_copy: Vec<Box<dyn Foo>> = v.to_vec();
v_copy.sort_by_key(|o| o.value());
for val in v_copy {
println!("{}", val.value());
}
}
Grrr!!
error[E0277]: the trait bound `dyn Foo: Clone` is not satisfied
--> src/main.rs:45:43
|
45 | let mut v_copy: Vec<Box<dyn Foo>> = v.to_vec();
| ^^^^^^ the trait `Clone` is not implemented for `dyn Foo`
|
= note: required because of the requirements on the impl of `Clone` for `Box<dyn Foo>`
至少现在我们有了一些线索。
这里发生了什么?
嗯,就像编译器所说的那样,
dyn Foo
没有实现
Clone
特性。这意味着
Box<dyn Foo>
和
Vec<Box<dyn Foo>>
也都没有实现
Clone
。
然而,
&Vec<Box<dyn Foo>>
实际上是实现了
Clone
的。这是因为你可以拥有任意数量的共享引用 - 共享(非可变)引用是
Copy
的,并且每个
Copy
也是
Clone
。试一下:
fn main() {
let i: i32 = 123;
let r0: &i32 = &i;
let r1: &i32 = <&i32 as Clone>::clone(&r0);
}
因此,当我们写
v.clone()
时,编译器会询问:“是否有一个名为
clone()
的方法,它以类型为
&Vec<Box<dyn Foo>>
(
v
)的
self
作为参数?” 它首先在
Vec<Box<dyn Foo>>
上寻找这样的方法的实现(因为
Clone::clone()
接受
&self
,所以对于
Vec<Box<dyn Foo>>
,它接受
&Vec<Box<dyn Foo>>
)。不幸的是,这样的实现不存在,因此它执行了自动引用(Rust 中尝试调整方法接收者的过程的一部分,您可以在
这里 阅读更多信息),并针对
&&Vec<Box<dyn Foo>>
提出了同样的问题。现在我们找到了匹配项 -
<&Vec<Box<dyn Foo>> as Clone>::clone()
!这就是编译器调用的内容。
这段文字讲解了一个方法的返回类型是什么,它是 `
&Vec<Box<dyn Foo>>
`。这也是变量 `v_copy` 的类型。现在我们明白了为什么当我们指定另一种类型时,编译器会出错!我们还可以解密当我们没有指定类型时的错误信息:我们要求编译器在 `
&Vec<Box<dyn Foo>>
` 上调用 `sort_by_key()` 方法,但该方法需要一个
&mut Vec<Box<dyn Foo>>
(确切地说是
&mut [Box<dyn Foo>]
,但不重要,因为
Vec<T>
可以强制转换为
[T]
)!
我们还可以理解有关冗余 mut 的警告:我们从未更改引用,因此不需要将其声明为可变的!
当我们调用
to_vec()
时,编译器并没有混淆:{{
to_vec()
}}要求向量的项实现{{
Clone
}}({{
where T: Clone
}}),而对于{{
Box<dyn Foo>
}},这并没有发生。崩溃。
现在解决方案应该很清楚了:我们只需要{{
Box<dyn Foo>
}}实现{{
Clone
}}。
只是?
我们可能会想到的第一件事是给{{
Foo
}}一个超级特征{{
Clone
}}:
trait Foo: Clone {
fn value(&self) -> i32;
}
#[derive(Clone)]
struct Bar { }
不起作用:
error[E0038]: the trait `Foo` cannot be made into an object
--> src/main.rs:33:31
|
33 | fn sort_and_print(v: &Vec<Box<dyn Foo>>) {
| ^^^^^^^ `Foo` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https:
--> src/main.rs:1:12
|
1 | trait Foo: Clone {
| --- ^^^^^ ...because it requires `Self: Sized`
| |
| this trait cannot be made into an object...
嗯,看起来
Clone
确实需要Sized
。为什么?
嗯,因为为了克隆某个东西,我们需要返回它本身。我们可以返回
dyn Foo
吗?不行,因为它的大小可以是任意的。
所以,让我们尝试手动实现
Box<dyn Foo>
的
impl Clone
(即使
Box
没有在我们的crate中定义,因为它是一个基本类型,而
Foo
是本地定义的(定义在我们的crate中))。
impl Clone for Box<dyn Foo> {
fn clone(self: &Box<dyn Foo>) -> Box<dyn Foo> {
}
}
“我们怎么能克隆一个可以是任何东西的东西呢?”显然,我们需要将其转发给其他人。还有谁?那些知道如何克隆这个东西的人。在
Foo
中定义一个方法吗?
trait Foo {
fn value(&self) -> i32;
fn clone_dyn(&self) -> Box<dyn Foo>;
}
impl Foo for Bar {
fn value(&self) -> i32 {
self.x
}
fn clone_dyn(&self) -> Box<dyn Foo> {
Box::new(self.clone())
}
}
现在!
impl Clone for Box<dyn Foo> {
fn clone(&self) -> Self {
self.clone_dyn()
}
}
它工作了!
fn sort_and_print(v: &Vec<Box<dyn Foo>>) {
let mut v_copy = v.clone();
v_copy.sort_by_key(|o| o.value());
for val in v_copy {
println!("{}", val.value());
}
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d6e871711146bc3f34d9710211b4a1dd
注意:@dtonlay 的
dyn-clone crate 推广了这个想法。
let mut v_copy: Vec<_> = v.iter().collect()
来缩短复制的过程。更好的问题是,当你只在Vec
中放置一个类型时,为什么需要动态分派?这将摆脱盒子。你也可以给sort_and_print
一个获取值的闭包,并完全摆脱特质:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0657ffcad6e4a78c1b73917d4f7f22c4 - user4815162342Vec<&Box<dyn Foo>>
而不是Vec<Box<dyn Foo>>
,但这不是问题。这也回答了问题1。那么,在这些情况下,.iter().collect()
是标准方法吗? - George MarcusVec<&Box<dyn Foo>>
,而.iter().collect()
只是更简洁的做法。let x: Vec<_> = <some iterator>.collect()
是创建一个你要迭代的向量的标准 Rust 短语。 - user4815162342