簡體   English   中英

隱藏在 Rust 中的代理和實現模式?

[英]Patterns for proxying and implementation hiding in Rust?

所以我是一位經驗豐富的開發人員,對 Rust 非常陌生,在 Java 方面的專家 - 但從匯編語言開始,所以我得到了 memory 和分配器,並且已經編寫了足夠好的編譯器和借用檢查器

我決定移植一個我在 Java 中編寫的非常有用的、基於高性能位集的圖形庫,既可以在更大的最終項目中使用它,也因為它非常有用。 因為它是位集中的所有 integer 位置,並且如果你想要一個 object 圖表你 map 到一個數組或任何其他索引嘗試嘗試將對象,這將是構建一個巨大的樹在 Rust 中執行。 我要解決的問題要簡單得多 - 如此簡單,我覺得我必須錯過一個明顯的模式來解決它:

查看可用於 Rust 的各種位集庫, FixedBitSet似乎很合適。 但是,我寧願不通過我的 API 公開它,並將我的庫的每個使用者不可撤銷地綁定到usize (這很好,但是,它也可以用於交換,例如,由原子支持的實現;並嫁給使用可能不理想,但 FixedBitSet 是)。

在 Java 中,您只需創建一個包裝具體類型實例的接口,並公開您想要的功能,隱藏實現類型。 在 Rust 中,您具有特征,因此很容易實現:

pub trait Bits<'i, S: Sized + Add<S>> {
    fn size(&'i self) -> S;
    fn contains(&'i self, s: S) -> bool;
...
impl<'i, 'a> Bits<'i, usize> for FixedBitSet {
    fn size(&'i self) -> usize {
        self.len()
    }
    fn contains(&'i self, s: usize) -> bool {
        FixedBitSet::contains(self, s)
    }
...

這有點難看,如果我不想公開FixedBitSet ,一切都必須返回Box<dyn Bits<'a, usize> + 'a> ,但現在就這樣 - 盡管這會產生自己的問題編譯器不知道dyn Bits...

到目前為止,一切都很好。 有趣的地方(我說這是一個非常簡單的問題嗎?)是代理一個迭代器 Rust 迭代器似乎不可撤銷地綁定到具有關聯類型的具體 Trait 類型 所以你不能真正抽象它(嗯,有點,通過Box<dyn Iterator<Item=usize> + 'a> where... ,看起來有可能創建一個擴展迭代器的特征,也上面有一個type Item ,並為u64 u32 usize和?? 實現它,也許編譯器會合並特征的type Item成員?)。 而且據我所知,您也不能將 trait 實現方法中的返回類型縮小到 trait 指定的其他類型。

由於 FixedBitSet 中用於迭代集合位的Ones類型有自己的生命周期,這變得更加復雜 - 但 Rust 的 Iterator 沒有 - 所以任何通用的 Iterator 實現都需要返回一個范圍為該生命周期的迭代器,而不是'_或迭代器與創建它的事物相比存在多長時間會存在問題。

我能想出的最整潔的東西 - 這一點都不整潔 - 在嘗試了各種容器的迭代器(向基值添加偏移量的 Bits 的實現也很有用)暴露它之后,是這樣的:

pub trait Biterable<'a, 'b, S: Sized + Add<S>, I: Iterator<Item = S> + 'b>  where 'a: 'b {
    fn set_bits<'c>(&'a mut self) -> I where I: 'c, 'b: 'c;
}

這是足夠可實施的:

impl<'a, 'b> Biterable<'a, 'b, usize, Ones<'b>> for FixedBitSet where 'a: 'b {
    fn set_bits<'c>(&'a mut self) -> Ones<'b> where Ones<'b>: 'c, 'b: 'c {
        self.ones()
    }
}

但是,我們知道我們將要處理 Boxes。 所以,我們需要一個實現。 偉大的! 類似impl<'a, 'b> Biterable<'a, 'b, usize, Ones<'b>> for Box<FixedBitSet> where 'a: 'b {是可實現的。 BUUUUUUT ,如果我們不在任何地方公開FixedBitSet ,那將不會返回任何東西 - 我們需要Box<dyn Bits<...> +...> 為此我們最終進入了一個鏡子大廳,在上面寫下越來越巴洛克和可怕(且無法編譯)的變體

impl<'a, 'b, B> Biterable<'a, 'b, usize, &'b mut dyn Iterator<Item=usize>>
   for Box<dyn B + 'a> where 'a: 'b, B : Bits<'a, usize> + Biterable<'a, 'b, usize, Ones<'b>> {

徒勞地尋找可以編譯和工作的東西(這失敗了,因為雖然BitsBiterable是特征,但顯然Biterable + Bits不是特征)。 說真的 - 一個無狀態的、不需要分配的包裝器,用於對該事物的一次調用返回對該事物的一次調用,只是不將該事物的類型暴露給調用者。 而已。 Java 等效項將是Supplier<T> a =...; return () -> a.get(); Supplier<T> a =...; return () -> a.get();

我必須把這個問題想錯了。 如何?

看起來你確實把事情復雜化了。 您有很多似乎沒有必要的生命周期注釋。 是一個簡單的實現(忽略通用S ):

use fixedbitset::FixedBitSet; // 0.2.0

pub trait Bits {
    fn size(&self) -> usize;
    fn contains(&self, s: usize) -> bool;
    fn set_bits<'a>(&'a mut self) -> Box<dyn Iterator<Item = usize> + 'a>;
}

impl Bits for FixedBitSet {
    fn size(&self) -> usize {
        self.len()
    }
    fn contains(&self, s: usize) -> bool {
        self.contains(s)
    }
    fn set_bits<'a>(&'a mut self) -> Box<dyn Iterator<Item = usize> + 'a> {
        Box::new(self.ones())
    }
}

pub fn get_bits_from_api() -> impl Bits {
    FixedBitSet::with_capacity(64)
}

如果您希望索引類型也是匿名的,請將其設為關聯類型,並且在公開您的Bits不定義它

use fixedbitset::FixedBitSet; // 0.2.0

pub trait Bits {
    type Idx: std::ops::Add<Self::Idx>;

    fn size(&self) -> Self::Idx;
    fn contains(&self, s: Self::Idx) -> bool;
    fn set_bits<'a>(&'a self) -> Box<dyn Iterator<Item = Self::Idx> + 'a>;
}

impl Bits for FixedBitSet {
    type Idx = usize;

    fn size(&self) -> Self::Idx {
        self.len()
    }
    fn contains(&self, s: Self::Idx) -> bool {
        self.contains(s)
    }
    fn set_bits<'a>(&'a self) -> Box<dyn Iterator<Item = Self::Idx> + 'a> {
        Box::new(self.ones())
    }
}

pub fn get_bits_from_api() -> impl Bits {
                           // ^^^^^^^^^ doesn't have <Idx = usize>
    FixedBitSet::with_capacity(64)
}

fn main() {
    let bits = get_bits_from_api();

    // just a demonstration that it compiles
    let size = bits.size();
    if bits.contains(size) {
        for indexes in bits.set_bits() {
            // ...
        }
    }
}

盡管出於多種原因,我強烈反對這樣做。 1)您需要更多的約束,而不僅僅是Add才能遠程使用。 2)您在impl Bits方面受到嚴重限制; 它沒有完全定義,因此您不能擁有dyn Bits或將其存儲在結構中。 3)我認為在這方面通用並沒有多大好處。

暫無
暫無

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

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