[英]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.