当关联类型没有大小时,如何避免需要`std :: marker :: Sized`?

lef*_*ead 5 rust

背景

我有一种情况,我想抽象两种不同的操作模式SparseDense.我选择哪一个是编译时决定.

与这些模式正交我有很多Kernels.内核的实现细节和签名在两种模式之间不同,但每种模式具有相同的内核.内核将在运行时根据模型文件确定.

我现在想创建一个BlackBox处理模式和内核的方法.

简化代码

我删除了其他内核和稀疏模式.

pub struct XKernel;

pub trait KernelDense {
    fn compute_dense(&self, vectors: &[f32]);
}

impl KernelDense for XKernel {
    fn compute_dense(&self, vectors: &[f32]) {}
}

pub trait KernelCompute<V> {
    fn just_compute_it(&self, vectors: &[V]);
}

impl KernelCompute<f32> for (dyn KernelDense + 'static) {
    fn just_compute_it(&self, v: &[f32]) {
        self.compute_dense(v);
    }
}

pub trait Generalization {
    type V: 'static;

    type OperatorType: KernelCompute<Self::V>;

    fn set_kernel(&self, x: Box<Self::OperatorType>);

    fn compute(&self, v: &[Self::V]);
}

pub struct DenseVariant {
    x: Box<KernelDense>,
}

impl Generalization for DenseVariant {
    type V = f32;
    type OperatorType = KernelDense;

    fn set_kernel(&self, x: Box<KernelDense>) {}

    fn compute(&self, v: &[Self::V]) {
        self.x.compute_dense(v);
    }
}

struct BlackBox<'a, T>
where
    T: Generalization,
{
    computer: T,
    elements: &'a [T::V],
}

impl<'a, T> BlackBox<'a, T>
where
    T: Generalization,
{
    fn runtime_pick_operator_and_compute(&mut self) {
        self.computer.set_kernel(Box::new(XKernel));
        let s = self.elements.as_ref();
        self.computer.compute(s);
    }
}

fn main() {
    // What I eventually want to do:
    // let black_box = BlackBox::<DenseVariant>::new();
    // black_box.runtime_pick_operator_and_compute();
}
Run Code Online (Sandbox Code Playgroud)

操场

上面的代码产生错误

error[E0277]: the size for values of type `(dyn KernelDense + 'static)` cannot be known at compilation time
  --> src/main.rs:35:6
   |
35 | impl Generalization for DenseVariant {
   |      ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `(dyn KernelDense + 'static)`
   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-sized>
Run Code Online (Sandbox Code Playgroud)

我尝试添加大量的: Sized(例如,给出BlackBox一个where T: Generalization + Sized,最终只产生不同的错误.

问题

  1. 我怎样才能实现std::marker::Sized(dyn KernelDense + 'static)/使这个程序编译解决上述的用意何在?
  2. 为什么编译器更在乎GeneralizationSized(甚至当我添加T: Generalization + SizedBlackBox)?是不是BlackBox(唯一使用的Generalization)被单形化为Generalization(例如DenseVariant)然后明显具有大小的东西Box

tre*_*tcl 7

错误消息令人困惑.该^^^s的(误导)指向Generalization,但实际的错误说dyn KernelDense,这是OperatorType.因此,OperatorType真正需要的是Sized.关联类型(如泛型类型参数)具有隐式Sized绑定,除非您通过添加?Sized而另外指定:

pub trait Generalization {
    ...
    type OperatorType: ?Sized + KernelCompute<Self::V>;
    ...
}
Run Code Online (Sandbox Code Playgroud)

但你会立即遇到另一个问题(游乐场):

error[E0308]: mismatched types
  --> src/main.rs:59:47
   |
59 |         self.computer.set_kernel(Box::new(XKernel));
   |                                           ^^^^^^^ expected associated type, found struct `XKernel`
   |
   = note: expected type `<T as Generalization>::OperatorType`
              found type `XKernel`
Run Code Online (Sandbox Code Playgroud)

其中,如果你在一行之间阅读,基本上是编译器说"我该怎么做Box<XKernel>?我需要一个Box<T::OperatorType>,我甚至不知道它是什么T!"

这应该是有道理的.因为没有规则禁止在哪里添加新类型的变体,OperatorType比方说str:

struct StringyVariant;

impl Generalization for StringyVariant {
    type V = f32;
    type OperatorType = str;

    fn set_kernel(&self, x: Box<str>) {}

    fn compute(&self, v: &[f32]) {}
}

impl KernelCompute<f32> for str {
    fn just_compute_it(&self, vectors: &[f32]) {}
}
Run Code Online (Sandbox Code Playgroud)

没有规则禁止这些implS,但它是不可能的,强制Box<XKernel>进入Box<str>,所以毯子implBlackBox必须是错误的.它缺少一项要求:Box<XKernel>可以强制要求的要求Box<T::OperatorType>.

在稳定的Rust(从1.28开始)中,没有办法将此要求写为特征限制,因此您必须编写两个impls(即一个for BlackBox<DenseVariant>和一个for BlackBox<SparseVariant>),或者可能找到其他方法(比如使用From而不是强制) ).

但是,在每晚的Rust中,你可以通过CoerceUnsized绑定和额外的方法as _来解决原始问题,以便向编译器提示它应该强制执行有意义的操作:

// at top of file
#![feature(coerce_unsized)]

use std::ops::CoerceUnsized;

impl<'a, T> BlackBox<'a, T>
where
    T: Generalization,
    Box<XKernel>: CoerceUnsized<Box<T::OperatorType>>,
{
    fn runtime_pick_operator_and_compute(&mut self) {
        self.computer.set_kernel(Box::new(XKernel) as _);
        let s = self.elements.as_ref();
        self.computer.compute(s);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是在操场上.