我想实现一个函数,它接受不可变的&Vec引用,制作副本,对值进行排序并打印它们。
这是主要代码。
trait Foo {
fn value(&self) -> i32;
}
struct Bar {
x: i32,
}
impl Foo for Bar {
fn value(&self) -> i32 {
self.x
}
}
fn main() {
let mut a: Vec<Box<dyn Foo>> = Vec::new();
a.push(Box::new(Bar { x: 3 }));
a.push(Box::new(Bar { x: 5 }));
a.push(Box::new(Bar { x: 4 }));
let b = &a;
sort_and_print(&b);
}
Run Code Online (Sandbox Code Playgroud)
我能够让它发挥作用的唯一方法就是这个
fn sort_and_print(v: &Vec<Box<dyn Foo>>) {
let mut v_copy = Vec::new();
for val in v {
v_copy.push(val);
}
v_copy.sort_by_key(|o| o.value());
for val in v_copy {
println!("{}", val.value());
}
}
Run Code Online (Sandbox Code Playgroud)
不过,我想了解这里发生了什么,并让代码更短。
如果我尝试更改let mut v_copy = Vec::new();,let mut v_copy: Vec<Box<dyn Foo>> = Vec::new();则会导致各种错误,我不知道如何修复。
如何使该版本正常工作?为什么它与第一个版本不同?
更接近我正在寻找的东西是这样的。
let mut v_copy = v.clone();但这行不通。这个版本可以修复吗?
Cha*_*man 10
首先,我们来注释一下类型:
fn sort_and_print(v: &Vec<Box<dyn Foo>>) {
let mut v_copy: Vec<&Box<dyn Foo>> = Vec::new();
for val /* : &Box<dyn Foo> */ in v {
v_copy.push(val);
}
v_copy.sort_by_key(|o: &&Box<dyn Foo>| <dyn Foo>::value(&***o));
for val /* : &Box<dyn Foo> */ in v_copy {
println!("{}", <dyn Foo>::value(&**val));
}
}
Run Code Online (Sandbox Code Playgroud)
迭代&Vec<T>会产生一个迭代器&T(与方法相同.iter())。
现在我们可以看到,我们可以通过调用.into_iter()onv和 then .collect()(这就是for循环的作用)或替换into_iter()with iter()(这更惯用,因为我们正在遍历引用)将其转换为迭代器:
fn sort_and_print(v: &Vec<Box<dyn Foo>>) {
let mut v_copy: Vec<&Box<dyn Foo>> = v.iter().collect(); // You can omit the `&Box<dyn Foo>` and replace it with `_`, I put it here for clarity.
v_copy.sort_by_key(|o| o.value());
for val in v_copy {
println!("{}", val.value());
}
}
Run Code Online (Sandbox Code Playgroud)
然而,我们仍然只有参考文献的副本 ( &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());
}
}
Run Code Online (Sandbox Code Playgroud)
...编译器对我们大喊大叫:
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
Run Code Online (Sandbox Code Playgroud)
什么???????????
好吧,让我们尝试指定类型。它可以让编译器变得更聪明。
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());
}
}
Run Code Online (Sandbox Code Playgroud)
没有。
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>>`
Run Code Online (Sandbox Code Playgroud)
好吧,让我们使用编译器的建议:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
咕噜!!
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>`
Run Code Online (Sandbox Code Playgroud)
至少我们现在有了一些线索。
这里发生了什么?
好吧,就像编译器所说的那样,dyn Foo没有实现该Clone特征。这意味着两者都没有Box<dyn Foo>,而且也是如此Vec<Box<dyn Foo>>。
然而,&Vec<Box<dyn Foo>>实际上确实impl Clone。这是因为您可以拥有任意数量的共享引用 - 共享(不可变)引用是Copy,并且每个Copy引用也是Clone。尝试一下:
fn main() {
let i: i32 = 123;
let r0: &i32 = &i;
let r1: &i32 = <&i32 as Clone>::clone(&r0);
}
Run Code Online (Sandbox Code Playgroud)
因此,当我们编写 时v.clone(),编译器会询问“是否有一个名为( )clone()类型self的方法?” 它首先在impl for上查找此类方法(因为需要,所以 for它需要)。不幸的是,这样的 impl 不存在,所以它发挥了 autoref 的魔力(尝试在 Rust 中调整方法接收器的过程的一部分,您可以在此处阅读更多内容),并针对. 现在我们确实找到了匹配 - !这就是编译器所说的。&Vec<Box<dyn Foo>>vCloneVec<Box<dyn Foo>>Clone::clone()&selfVec<Box<dyn Foo>>&Vec<Box<dyn Foo>>&&Vec<Box<dyn Foo>><&Vec<Box<dyn Foo>> as Clone>::clone()
该方法的返回类型是什么?出色地,&Vec<Box<dyn Foo>>。这将是 的类型v_copy。现在我们明白为什么当我们指定另一种类型时,编译器会变得疯狂!当我们没有指定类型时,我们也可以解密错误消息:我们要求编译器调用sort_by_key()a &Vec<Box<dyn Foo>>,但这个方法需要 ( &mut Vec<Box<dyn Foo>>,&mut [Box<dyn Foo>]准确地说,但这并不重要,因为Vec<T>可以强制转换为[T])!
我们还可以理解有关冗余的警告mut:我们从不更改引用,因此无需将其声明为可变!
当我们调用to_vec()OTOH 时,编译器并没有感到困惑:to_vec()需要向量的项来实现Clone( where T: Clone),这对于 来说不会发生Box<dyn Foo>。繁荣。
现在解决方案应该很清楚:我们只需要Box<dyn Foo>impl Clone。
只是?...
我们首先想到的可能是赋予Foo一个超级特质Clone:
fn main() {
let i: i32 = 123;
let r0: &i32 = &i;
let r1: &i32 = <&i32 as Clone>::clone(&r0);
}
Run Code Online (Sandbox Code Playgroud)
不工作:
trait Foo: Clone {
fn value(&self) -> i32;
}
#[derive(Clone)]
struct Bar { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
嗯,看来Clone确实需要Sized。为什么?
嗯,因为为了克隆某些东西,我们需要返回它本身。我们可以回来吗dyn Foo?不,因为它可以是任何大小。
因此,让我们尝试impl Clone for Box<dyn Foo>手动(即使Box没有在我们的板条箱中定义,我们也可以做到这一点,因为它是基本类型并且Foo是本地的(在我们的板条箱中定义))。
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://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> src/main.rs:1:12
|
1 | trait Foo: Clone {
| --- ^^^^^ ...because it requires `Self: Sized`
| |
| this trait cannot be made into an object...
Run Code Online (Sandbox Code Playgroud)
我们怎么能克隆任何东西呢?显然我们需要将其转发给其他人。还有谁?知道如何克隆这个东西的人。一种方法Foo?
impl Clone for Box<dyn Foo> {
fn clone(self: &Box<dyn Foo>) -> Box<dyn Foo> {
// Now... What, actually?
}
}
Run Code Online (Sandbox Code Playgroud)
现在!
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()) // Forward to the derive(Clone) impl
}
}
Run Code Online (Sandbox Code Playgroud)
有用!!
impl Clone for Box<dyn Foo> {
fn clone(&self) -> Self {
self.clone_dyn()
}
}
Run Code Online (Sandbox Code Playgroud)
注意: @dtonlay 的dyn -clone crate 概括了这个想法。
| 归档时间: |
|
| 查看次数: |
3508 次 |
| 最近记录: |