generics - 定义通用特征时出现未知大小编译错误

标签 generics rust traits

作为 Rust 编程的初学者,我对 Size 特征与通用特征的结合有点困惑。 我的自定义特征定义了一种方法,该方法接受两个对象并将它们组合成一个新对象。由于原始对象在组合后就会过期,因此方法应该取得参数的所有权。 我还返回带有自定义错误类型的结果,因为组合可能会失败,并且我想向调用者提供失败的原因。

有问题的特征是:

pub trait Combinable<T> {
    fn combine(self, other: T) -> CombinationResult<T>;
}
struct CombinationError;

type CombinationResult<T> = std::result::Result<dyn Combinable<T>, CombinationError>

编译此代码时出现以下错误:

error[E0277]: the size for values of type `(dyn Combinable<T> + 'static)` cannot be known at compilation time
    |
7   |     fn combine(self, other: T) -> CombinationResult<T>;
    |                                   ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time

    |
241 | pub enum Result<T, E> {
    |                 - required by this bound in `std::result::Result`
    |
    = help: the trait `Sized` is not implemented for `(dyn Combinable<T> + 'static)`

我尝试将泛型类型参数 T 限制为 Sized (即 pub trait Combinable<T: Sized> )但这不起作用。我也在类型别名声明中尝试了相同的方法,也无济于事。

我是否遗漏/忽略了某些东西?

最佳答案

dyn Combinable<T>只能用在引用后面( &BoxRc …)。由于您想要转让组合项目的所有权,因此您有两种选择:

  • 最简单的方法是将结果装箱:type CombinationResult<T> = std::result::Result<Box<dyn Combinable<T>>, CombinationError>;但这会以内存分配和间接的形式增加一些开销。

  • 或者您可以使用泛型类型来避免间接:

pub trait Combinable<T> {
    type Combined;
    fn combine(self, other: T) -> CombinationResult<Self::Combined>;
}
pub struct CombinationError;

type CombinationResult<T> = std::result::Result<T, CombinationError>;

Playground

第二个解决方案的工作原理是将组合项移动到返回值中。以下是我们在没有泛型、特征和错误处理的情况下如何做到这一点:

struct ItemA {}
struct ItemB {}
impl ItemA {
    fn combine (self, other: ItemB) -> CombinedAB {
        CombinedAB { a: self, b: other }
    }
}

struct CombinedAB {
    a: ItemA,
    b: ItemB,
}

Playground

请注意 combine 返回的类型方法取决于输入的类型。如果我们想抽象combine方法到特征中,当我们声明特征时,我们无法修复返回类型,因为我们还不知道实现中将使用的类型。因此,我们使用关联类型,这是一种说法:“该类型将由实现指定”。这是使用特征和泛型的相同示例:

pub trait Combinable<T> {
    type Combined;
    fn combine(self, other: T) -> Self::Combined;
}

struct ItemA {}
struct ItemB {}

struct CombinedAB {
    a: ItemA,
    b: ItemB,
}

impl Combinable<ItemB> for ItemA {
    type Combined = CombinedAB;
    fn combine (self, other: ItemB) -> CombinedAB {
        CombinedAB { a: self, b: other }
    }
}

Playground

请注意,我对参数使用了泛型类型,对返回值使用了关联类型。 This question提供更多详细信息并解释何时使用其中之一。

当然,如果 combine方法应该为所有输入返回相同的具体类型,然后您可以完全放弃关联的类型:

struct Combined {}
pub trait Combinable<T> {
    fn combine(self, other: T) -> Combined;
}

关于generics - 定义通用特征时出现未知大小编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65621903/

相关文章:

pointers - 为什么在对取消引用的枚举进行匹配时必须使用 ref?

具有特征的案例类的equals/hashCode的Scala语义

c# - 如何知道类型参数是否是动态的?

regex - 将所有正则表达式匹配捕获到一个向量中

c# - 从对象的常规列表中删除所有空条目

authentication - 在 git2-rs 中,克隆时如何进行身份验证?

generics - 如何将通用特征设置为函数参数的类型?

新类型的 Rust 特征

c# - typeof(t).GetProperties() 当 t 是从另一个派生的接口(interface)时

scala - 如何读取复杂的 scala 类型定义,如 `` `type Pipe[F[_], -I, +O] = Stream[F, I] => Stream[F, O] `` `