簡體   English   中英

嘗試對特征對象進行裝箱時出現“預期特征A,找到&A”

[英]“Expected trait A, found &A” when trying to box a trait object

我正在嘗試制作一個特征,該特征可以檢索(並返回對)另一個特征的一個特征對象,或者創建一個特征(並返回其盒裝版本),將選擇留給實現者(這意味着我需要將返回的對象的生存期限制為生產者的生存期)。 但是,我遇到了錯誤:

use std::borrow::Borrow;
use std::collections::HashMap;

trait A { 
    fn foobar(&self) {
        println!("!"); 
    } 
}

trait ProducerOrContainer {
    fn get_a<'a>(&'a self, name: &'a str) -> Option<Box<dyn A + 'a>>;
}

impl<'b, B: Borrow<A>> ProducerOrContainer for HashMap<&'b str, B> {
    fn get_a<'a>(&'a self, name: &'a str) -> Option<Box<dyn A + 'a>> {
        self.get(name).map(|borrow| Box::new(borrow.borrow()))
    }
}

錯誤是:

error[E0308]: mismatched types
  --> src/main.rs:20:9
   |
20 |         self.get(name).map(|borrow| Box::new(borrow.borrow()))
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait A, found &A
   |
   = note: expected type `std::option::Option<std::boxed::Box<dyn A + 'a>>`
              found type `std::option::Option<std::boxed::Box<&dyn A>>`

這讓我感到困惑,因為我希望&A也等於A 我嘗試將impl<'a> A for &'a A ,但這也無濟於事。 有沒有什么辦法解決這一問題?

...可以檢索(或返回對)另一個特征的一個特征對象,或創建一個(並返回其盒裝版本)。

有了此要求, Box將無法工作。 Box 擁有其數據,但有時您已借用了數據,無法移動。

標准庫中有一個類型叫做Cow ,它是關於值是借來的還是擁有的抽象。 但是,它在這里可能不太適合您,因為它不允許您將數據作為Box ,並且還要求您的數據類型必須實現ToOwned

但是我們可以接受您的要求並將其直接建模為enum

enum BoxOrBorrow<'a, T: 'a + ?Sized> {
    Boxed(Box<T>),
    Borrowed(&'a T),
}

並通過實現Deref使其符合人體工程學:

use std::ops::Deref;

impl<'a, T> Deref for BoxOrBorrow<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        match self {
            BoxOrBorrow::Boxed(b) => &b,
            BoxOrBorrow::Borrowed(b) => &b,
        }
    }
}

這使您可以將自定義BoxOrBorrow類型與其他任何引用一樣對待-您可以使用*取消引用它,或者將其傳遞給任何期望引用T函數。

這是您的代碼如下所示:

trait ProducerOrContainer {
    fn get_a<'a>(&'a self, name: &'a str) -> Option<BoxOrBorrow<'a, dyn A + 'a>>;
}

impl<'b, B: Borrow<dyn A>> ProducerOrContainer for HashMap<&'b str, B> {
    fn get_a<'a>(&'a self, name: &'a str) -> Option<BoxOrBorrow<'a, dyn A + 'a>> {
        self.get(name)
            .map(|b| BoxOrBorrow::Borrowed(b.borrow()))
    }
}

您可以通過實現使原來的代碼編譯A&'_ dyn A和添加有明確的轉換:

self.get(name).map(|borrow| Box::new(borrow.borrow()) as Box<dyn A>)

封鎖不是強制性的場所 編譯器查看閉包的內容以查看返回值是什么,並得出結論,它返回Box<&'a dyn A> 但是閉包本身不能從“函數返回Box<&'a dyn A> ”強制轉換為“函數返回Box<dyn A + 'a> ”,因為這些類型在結構上是不同的。 您添加了強制類型轉換,以告知編譯器您希望閉包首先返回Box<dyn A>

但這有點愚蠢。 Box荷蘭國際集團的基准是完全沒有必要在這里,並澆鑄到Box<dyn A>只是增加了對呼叫者間接另一個層次。 這將是更好的回報封裝“ 無論是盒裝特質的對象, 或者性狀對象的引用”的概念,作為一種彼得·霍爾的答案描述。


在帶有通用關聯類型(“ GAT”)的Rust的未來版本中,可以使返回類型成為ProducerOrContainer的關聯類型,如下所示:

trait ProducerOrContainer {
    type Result<'a>: A;
    fn get_a<'a>(&'a self, name: &'a str) -> Option<Result<'a>>;
}

使用此特征定義,實現ProducerOrContainer每種類型都可以選擇其返回的類型,因此可以為某些impl選擇Box<dyn A>為其他暗示選擇&'a dyn A 但是,這在當前的Rust(1.29)中是不可能的。

暫無
暫無

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

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