rust - Rust 中隐藏的代理和实现模式?
问题描述
所以我是一位经验丰富的开发人员,对 Rust 很陌生,是 Java 专家——但我是从汇编语言开始的,所以我得到了内存和分配,并且写了足够多的编译器东西来很好地理解借用检查器。
我决定移植一个我用 Java 编写的非常有用的、基于位集的高性能图形库,既是为了在更大的最终项目中使用它,也是因为它非常有用。因为它是位集中的所有整数位置,并且如果您想要一个对象图,您可以将索引映射到一个数组或其他任何东西 - 我不会纠结于构建一个相互引用的巨大对象树,这将是一团糟在 Rust 中做。我要解决的问题要简单得多 - 如此简单,我觉得我必须错过一个明显的模式来解决它:
查看可用于 Rust 的各种位集库,FixedBitSet似乎很合适。 但是,我宁愿不通过我的 API 公开它,并将我的库的每个使用者不可撤销地绑定到 FixedBitSet(这很好,但是,换入例如由原子支持的实现也很有用;并且与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 类型。所以你不能真正抽象它(好吧,有点像 via Box<dyn Iterator<Item=usize> + 'a> where ...
,看起来有可能创建一个扩展迭代器并在其上也有 a 的特征,并为 ,和??type Item
实现它也许是编译器合并特征的成员?)。而且据我所知,您也不能将 trait 实现方法中的返回类型缩小到 trait 指定的其他类型。u32
u64
usize
type Item
由于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>> {
徒劳地寻找可以编译和工作的东西(这失败了,因为 whileBits
和Biterable
是特征,显然Biterable + Bits
不是特征)。说真的 - 一个无状态的、不需要分配的包装器,用于对该事物的一次调用返回对该事物的一次调用,只是不将该事物的类型暴露给调用者。而已。Java等价物是Supplier<T> a = ...; return () -> a.get();
我必须把这个问题想错了。如何?
解决方案
看起来你确实把事情复杂化了。您有很多似乎没有必要的生命周期注释。这是一个简单的实现(忽略 generic 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)我认为在这方面通用并没有多大好处。
推荐阅读
- python - 无法将复数转换为浮点数
- android - 在导航抽屉菜单项中更改开关颜色
- sonarqube - 错误:未指定属性“sonar.cfamily.build-wrapper-output”Sonarqube
- sql-server-2012 - SQLSTATE[08001]:[Microsoft][ODBC Driver 11 for SQL Server]TCP 提供程序:无法建立连接,因为目标计算机主动拒绝
- python - Django 开发服务器无法访问
- python - 邀请使用返回无
- javascript - 为什么 JQuery 需要一个全局“窗口”对象?
- angular - 将 Cheerio.js 添加到 Angular 6 项目?
- python - 在 Pandas 中搜索条件的第一个实例
- excel - 如何根据另一个单元格的内容在excel中创建新单元格