首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当关联类型没有大小时,如何避免需要``std::marker::size‘?

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

Stack Overflow用户
提问于 2018-08-12 20:03:21
回答 1查看 1.9K关注 0票数 6

背景

在这种情况下,我想抽象出两种不同的操作模式-- SparseDense。我选择哪一个是编译时的决定。

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

现在我想创建一个同时处理模式和内核的BlackBox

简化代码

我删除了额外的内核和稀疏模式。

代码语言:javascript
复制
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();
}

游乐场

上面的代码会产生错误。

代码语言:javascript
复制
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>

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

问题

  1. 我如何实现std::marker::Sized(dyn KernelDense + 'static) /使这个程序编译解决上述意图?
  2. 为什么编译器甚至关心GeneralizationSized (即使我将T: Generalization + Sized添加到BlackBox中)?难道BlackBox (使用Generalization的唯一一个)不是被单一化为Generalization (比如DenseVariant),而后者的大小显然是Box吗?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-08-13 01:18:07

错误信息令人困惑。^^^是(误导地)指向Generalization,但实际的错误是dyn KernelDense,即OperatorType。由于至少锈蚀1.50,您得到了一个更好的错误消息:

代码语言:javascript
复制
error[E0277]: the size for values of type `(dyn KernelDense + 'static)` cannot be known at compilation time
  --> src/main.rs:42:9
   |
24 |         type OperatorType: KernelCompute<Self::V>;
   |         ------------------------------------------ required by this bound in `Generalization::OperatorType`
...
42 |         type OperatorType = KernelDense;
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `(dyn KernelDense + 'static)`

因此,OperatorType才是真正需要成为Sized的东西。关联类型相似的泛型类型参数具有隐式Sized绑定,除非通过添加?Sized来另作指定:

代码语言:javascript
复制
pub trait Generalization {
    ...
    type OperatorType: ?Sized + KernelCompute<Self::V>;
    ...
}

但是,您将立即遇到另一个问题(游乐场):

代码语言:javascript
复制
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`

如果你在字里行间读一点的话,那就是编译器在说“我对Box<XKernel>做了什么?我需要一个Box<T::OperatorType>,我甚至还不知道T是什么!”

这应该是有道理的。因为没有任何规则禁止在OperatorType所在的地方添加一种新的变体,比如说,str

代码语言:javascript
复制
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]) {}
}

没有任何规则禁止这些impl,但是不可能强迫Box<XKernel>进入Box<str>,所以BlackBox上的总括impl一定是错误的。它缺少了一个需求:Box<XKernel>可以强制进入Box<T::OperatorType>的要求。

在“稳定锈蚀”(从1.50开始)中,没有办法将这个需求写成一个特征绑定,所以您必须编写两个impl(一个用于BlackBox<DenseVariant>,另一个用于BlackBox<SparseVariant>),或者可能找到其他方法(比如使用From而不是强制)。

然而,在“夜间锈蚀”中,您可以使用CoerceUnsized绑定和额外的as _来解决最初的问题,以向编译器提示它应该强制执行一些有意义的内容:

代码语言:javascript
复制
// 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);
    }
}

这是在操场上。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51812394

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档