繁体   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