我希望实现一个函数,该函数接受不可变的&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);
}我唯一能做到的就是这样
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());
}
}但是,我想了解这里发生了什么,并使代码更短。
问题1
但是,如果我试图将let mut v_copy = Vec::new();更改为let mut v_copy: Vec<Box<dyn Foo>> = Vec::new();,则会导致各种错误,而我不知道如何修复这些错误。
我如何使这个版本工作,为什么它与第一个版本不同?
企图2
更接近我要找的东西就像这样。let mut v_copy = v.clone();,但这不管用。这个版本能修好吗?
发布于 2021-11-09 01:26:32
首先,让我们对以下类型进行注释:
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));
}
}在&Vec<T>上迭代会在&T上产生一个迭代器(与.iter()方法相同)。
现在,我们可以将其转换为迭代器,方法是在v上调用v,然后调用.collect() (这是for循环所做的),或者用iter()替换into_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());
}
}但是,我们仍然只有引用(&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());
}
}...the编译器对我们大喊:
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>>也是如此。
然而,&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>>类型的self (v)?”它首先在Clone impl上为Vec<Box<dyn Foo>>寻找这样的方法(因为Clone::clone()采用&self,所以对于Vec<Box<dyn Foo>>,它需要&Vec<Box<dyn Foo>>)。不幸的是,这种exist不存在,因此它具有自动引用的魔力(在Rust中尝试调整方法接收器的过程,您可以读取更多的这里),并对&&Vec<Box<dyn Foo>>提出同样的问题。现在我们找到了一个匹配的- <&Vec<Box<dyn Foo>> as Clone>::clone()!这就是编译器所调用的。
方法的返回类型是什么?好吧,&Vec<Box<dyn Foo>>。这将是v_copy的类型。现在我们明白为什么当我们指定另一种类型时,编译器变得疯狂了!当我们没有指定类型时,我们也可以解密错误消息:我们要求编译器在&Vec<Box<dyn Foo>>上调用&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>来推动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://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...嗯,看起来像Sized。为什么?
因为为了复制某种东西,我们需要回归自我。我们能还dyn Foo吗?不,因为它可以是任何大小的。
因此,让我们尝试手动impl Clone for Box<dyn Foo> (即使在我们的机箱中没有定义Box,因为它是一个基本类型,Foo是本地的(在我们的机箱中定义)),我们也可以这样做。
impl Clone for Box<dyn Foo> {
fn clone(self: &Box<dyn Foo>) -> Box<dyn Foo> {
// Now... What, actually?
}
}我们怎么能克隆出可以成为任何东西的东西?显然我们需要转发给其他人。还有谁?一个知道如何克隆这东西的人。一种基于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()) // Forward to the derive(Clone) impl
}
}现在!
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());
}
}备注: @dtonlay中的双克隆机箱推广了这一思想。
发布于 2021-11-09 16:20:09
您可以使用sort_and_print()缩短Iterator::collect()
fn sort_and_print(v: &[Box<dyn Foo>]) {
let mut v_copy: Vec<_> = v.iter().collect();
v_copy.sort_by_key(|o| o.value());
for val in v_copy {
println!("{}", val.value());
}
}顺便说一句,通过引用接受向量通常表示为接受一个片段,如前所述,所以上面的答案接受一个片段。
您可以通过使用来自sorted()机箱的itertools方法,或者在本例中是它的表亲sorted_by_key(),使其更短。
use itertools::Itertools;
fn sort_and_print(v: &[Box<dyn Foo>]) {
for val in v.iter().sorted_by_key(|o| o.value()) {
println!("{}", val.value());
}
}您几乎肯定不想克隆这个载体,因为它将涉及到一个深拷贝,即克隆每个Box<dyn Foo>,这是不必要的、潜在的昂贵的以及复杂的(正如在另一个答案中所解释的)。
https://stackoverflow.com/questions/69890183
复制相似问题