首页 > 解决方案 > 将未调整大小和基于动态调度的特征作为参数传递的问题

问题描述

我正在尝试编写一个Query相互馈送的传感器网络(下面称为)。

AQuery<A, B>接受类型 的输入项A,并输出 中的值B。它通过将它们提供给一个接收器来实现,该接收器Sink<B>公开适当的方法来消耗元素。

pub trait Sink<A> {
    fn init(&mut self);
    fn next(&mut self, item: A);
    fn end(&mut self);
}

pub trait Query<A,B>: {
    fn init(&mut self, sink: &mut dyn Sink<B>);
    fn next(&mut self, item: A, sink: &mut dyn Sink<B>);
    fn end(&mut self, sink: &mut dyn Sink<B>);
}

现在,给定 aQuery<A, B>和 a Query<B, C>,可以将它们组合成 a Query<A, C>。我将调用此操作PipelinePipeline下面,为了方便起见,我定义了一个构造函数。

pub struct Pipeline<'a, A, B, C, QAB: Query<A, B>, QBC: Query<B, C> > {
    q_ab: &'a mut QAB,
    q_bc: &'a mut QBC,
    _abc : PhantomData<(A, B, C)>
}

impl<'a, A, B, C, QAB: Query<A, B>, QBC: Query<B, C>> Pipeline<'a, A, B, C, QAB, QBC>{
    pub fn new(q1: &'a mut QAB, q2: &'a mut QBC) -> Self{
       Self {q_ab : q1, q_bc: q2, _abc:PhantomData}
    }
}

为了实现管道,我们需要一个中间接收器,它的唯一工作是将输出推Query<A, B>送到Query<B, C>

struct PushSink<'a, B, C, QBC : Query<B, C>, S : Sink<C> + ?Sized> {
    q_bc : &'a mut QBC,
    s_c : &'a mut S,
    _bc : PhantomData<(B, C)>
}

impl <'a, B, C, QBC: Query<B, C>, S: Sink<C> + ?Sized> PushSink<'a, B, C, QBC, S>{
    pub fn new(q_bc: &'a mut QBC, s_c : &'a mut S) -> Self{
        PushSink {q_bc : q_bc, s_c : s_c, _bc : PhantomData}
    }
}

impl<'a, B, C, QBC: Query<B, C>, S: Sink<C> + ?Sized> Sink<B> for PushSink<'a, B, C, QBC, S>{
    fn init(&mut self) {
        self.q_bc.init(self.s_c);
    }
    fn next(&mut self, item: B) {
        self.q_bc.next(item, self.s_c);
    }
    fn end(&mut self) {
        self.q_bc.end(self.s_c);
    }
}

最后,我们可以使用这个定义来定义如何Pipeline作为Query<A, C>

impl<'a, A, B, C, QAB: Query<A, B>, QBC: Query<B, C>> Query<A, C> for Pipeline<'a, A, B, C, QAB, QBC>{
    fn init(&mut self, sink: &mut dyn Sink<C>){
        self.q_ab.init(&mut PushSink::new(self.q_bc, sink))
    }
    fn next(&mut self, item: A, sink: &mut dyn Sink<C>){
        self.q_ab.next(item, &mut PushSink::new(self.q_bc, sink))
    }
    fn end(&mut self, sink: &mut dyn Sink<C>){
        self.q_ab.end(&mut PushSink::new(self.q_bc, sink))
    }
}

但是,这不起作用,因为

error[E0277]: the size for values of type `S` cannot be known at compilation time
  --> test.rs:47:23
   |
39 | impl<'a, B, C, QBC: Query<B, C>, S: Sink<C> + ?Sized> Sink<B> for PushSink<'a, B, C, QBC, S>{
   |                                  - this type parameter needs to be `std::marker::Sized`
...
47 |         self.q_bc.end(self.s_c);
   |                       ^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `S`
   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
   = note: required for the cast to the object type `dyn Sink<C>`

我该如何解决这种情况?

我知道如何在使用接口而不是特征的 Java 中实现相同的想法。它似乎在 Java 中没有问题。

另外,我想要一个基于特征的解决方案。我希望将Query<A, B>andSink<B>等作为特征而不是其他东西(例如,枚举数据类型)。

如果可能的话,我也想避免使用盒子。我更喜欢使用泛型和特征而不是框的解决方案。

我也尝试过将签名更改Query<,>

pub trait Query<A,B>: {
    fn init(&mut self, sink: &mut (dyn Sink<B> + ?Sized));
    fn next(&mut self, item: A, sink: &mut (dyn Sink<B> + ?Sized));
    fn end(&mut self, sink: &mut dyn Sink<B>);
}

但显然,这是不允许的

完整的源代码在这里,为方便起见

编辑:游乐场链接,感谢@eggyal

标签: genericsdynamicrustpolymorphismtraits

解决方案


推荐阅读