[英]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>> {
徒勞地尋找可以編譯和工作的東西(這失敗了,因為雖然Bits
和Biterable
是特征,但顯然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.