簡體   English   中英

如何克隆 Vec <Box<dyn Trait> &gt;?

[英]How can I clone a Vec<Box<dyn Trait>>?

我想實現一個函數,它接受一個不可變的&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(); 但這不起作用。 這個版本可以修復嗎?

首先,讓我們注釋類型:

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上調用.into_iter()然后.collect() (這是for循環所做的),或者用iter()替換into_iter() 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());
    }
}

...編譯器對我們大喊:

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());
    }
}

咕嚕!!

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>>實際上確實impl 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 ?” 它首先在Clone impl 上為Vec<Box<dyn Foo>>尋找這樣的方法(因為Clone::clone()需要&self ,所以對於Vec<Box<dyn Foo>>它需要&Vec<Box<dyn Foo>> )。 不幸的是,這樣的 impl 不存在,因此它發揮了 autoref 的魔力(嘗試在 Rust 中調整方法接收器的過程的一部分,您可以在此處閱讀更多內容),並對&&Vec<Box<dyn Foo>>提出同樣的問題&&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() ,OTOH 時,編譯器並沒有感到困惑: to_vec()需要向量的項來實現Clonewhere T: Clone ),而Box<dyn Foo>不會發生這種情況。 繁榮。

現在解決方案應該很清楚了:我們只需要Box<dyn Foo>來實現Clone

只是?...

我們可能會想到的第一件事是給Foo一個 supertrait 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...

嗯,看起來確實Clone需要Sized 為什么?

好吧,因為要克隆某些東西,我們需要返回自身。 我們可以返回dyn Foo嗎? 不,因為它可以是任何大小。

因此,讓我們嘗試手動impl Clone for Box<dyn Foo> (即使Box未在我們的 crate 中定義,因為它是基本類型,我們也可以這樣做)。

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());
    }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d6e871711146bc3f34d9710211b4a1dd

注意: @dtonlay 的dyn-clone crate 概括了這個想法。

您可以使用Iterator::collect()縮短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());
    }
}

操場

順便說一句,通過引用接受矢量通常更好表示為接受片, 按此處的說明,所以上述答案接受片。

您可以使用itertools箱中的sorted()方法使其更短:

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> ,這是不必要的,可能很昂貴,而且很復雜(如另一個答案中所述)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM