【问题标题】:Unknown size compile error when defining generic trait定义通用特征时出现未知大小编译错误
【发布时间】:2021-04-13 17:48:33
【问题描述】:

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

有问题的特征是:

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&lt;T: Sized&gt;),但没有奏效。我也在类型别名声明中尝试了同样的方法,也无济于事。

我是否遗漏/忽略了什么?

【问题讨论】:

    标签: generics rust traits


    【解决方案1】:

    dyn Combinable&lt;T&gt; 只能在引用后面使用(&amp;BoxRc...)。由于您想转让组合项目的所有权,您有两种选择:

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

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

    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;
    }
    

    【讨论】:

    • 我得到了第一个解决方案,但你能更深入地解释一下第二个解决方案吗?
    • 额外的解释很有帮助。谢谢!
    【解决方案2】:

    dyn Combinable&lt;T&gt; 是一个 unsized 类型(因为 trait 可以通过任何类型的类型实现,所以值的大小在编译时是未知的)。这反过来意味着Result&lt;dyn Combinable&lt;T&gt;, ...&gt; 也没有大小。 Unsized 类型相当有限,其中一个限制是它们不能按值返回。您只能通过某种间接方式(引用、指针等)有意义地处理未调整大小的类型

    通常的解决方法是将它们装箱

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

    【讨论】:

      猜你喜欢
      • 2021-11-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多