【问题标题】:Can I declare that implementations of a given trait should not be expected to be Sized?我可以声明不应该期望给定特征的实现是大小的吗?
【发布时间】:2018-05-23 08:18:04
【问题描述】:

我定义了一个 trait Foo,为 [u32] 实现了这个 trait,并写了一个函数 bar 以这个 trait 作为参数 (playground):

trait Foo {
    fn foo(&self) -> u32;
}

impl Foo for [u32] {
    fn foo(&self) -> u32 {
        self[0]
    }
}

fn bar<T>(f: &T) -> u32
where
    T: Foo,
{
    f.foo() + 1
}

fn main() {
    let f: &[u32] = &[42];
    bar(f);
}

这不会编译,因为bar 隐含地期望它的参数是Sized

error[E0277]: the trait bound `[u32]: std::marker::Sized` is not satisfied
  --> src/main.rs:20:5
   |
20 |     bar(f);
   |     ^^^ `[u32]` does not have a constant size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `[u32]`
note: required by `bar`
  --> src/main.rs:11:1
   |
11 | / fn bar<T>(f: &T) -> u32
12 | | where
13 | |     T: Foo,
14 | | {
15 | |     f.foo() + 1
16 | | }
   | |_^

我可以用 T: Foo + ?Sized 修复它,但是我必须为 每个 函数执行此操作,期待 Foo,这很痛苦...

我是否可以一劳永逸地声明 Foo 的实现不应该是 Sized?我在第 1 行尝试了trait Foo: ?Sized,但编译器抱怨它。

这个问题和Trait implementing Sized不一样。在那个问题中,Foo 参数被移动了,所以编译器想在编译时知道它的大小是很正常的。在我的例子中,参数是一个引用,所以它不需要 来调整大小——但编译器仍然隐含地假设它是,除非明确告知(使用+ ?Sized)。我想改变的是这个隐含的假设,对于这个特殊的特征。

【问题讨论】:

    标签: rust traits


    【解决方案1】:

    为什么编译器会抱怨trait Foo: ?Sized

    当你说每个Foo 都是Sized 时,你有点对自己隐瞒了真相。是的,每个Foo 都是Sized,但实际上每种类型在某个时候都有给定的大小。真正重要的信息是,您并没有说这个尺寸是多少。

    在这种情况下,您要求一个大小为[u32]

    error[E0277]: the trait bound `[u32]: std::marker::Sized` is not satisfied
      |
      | impl Foo for [u32] {
      |      ^^^ `[u32]` does not have a constant size known at compile-time
      |
      = help: the trait `std::marker::Sized` is not implemented for `[u32]`
    

    如需更多信息,请咨询this answerTrait implementing Sized

    为什么T 不能取消大小,因为我们只有一个参考?

    让我再次引用this answer Why does a reference to a trait in a generic function have to implement Sized? 的内容

    默认情况下,函数上的 所有 泛型类型隐式地具有 Sized 绑定,无论它们如何使用。您需要使用 ?Sized 明确选择退出该要求

    这将解决您的问题:

    fn bar<T>(f: &T) -> u32
    where
        T: Foo + ?Sized,
    {
        f.foo() + 1
    }
    

    Playground

    也可以为&amp;[u32] 实现Foo

    impl<'a> Foo for &'a [u32] {
        fn foo(&self) -> u32 {
            self[0]
        }
    }
    
    fn bar<T>(f: T) -> u32
    where
        T: Foo,
    {
        f.foo() + 1
    }
    

    Playground

    在这种特殊情况下,您甚至可以为 Foo 推广您的实现,它适用于 Vecs 和数组以及对这些类型的引用:

    impl<T: AsRef<[u32]>> Foo for T {
        fn foo(&self) -> u32 {
            self.as_ref()[0]
        }
    }
    

    使用新的impl Trait 语法,bar 在最后两种情况下可以缩短为

    fn bar(f: impl Foo) -> u32 {
        f.foo() + 1
    }
    

    Playground

    【讨论】:

    • 请注意,最后一个符号有些争议,但我不会在这个答案中深入探讨。
    • 感谢您的回答。关于。第一部分,我认为您错过了我的观点:我不是要强制执行 Foo: Sized,而是相反, Foo: ?Sized (即“Foo 可能不是 Sized”)。关于。第二部分,我有为 &[u32] 实现 Foo 的想法,但是我必须将 &&[u32] 传递给bar,这似乎有点尴尬......
    • @Pierre-Antoine 那么答案是否解决了您的问题?
    • 不完全是:它使特定示例编译,但我的目标是让编译器知道 Foo+?Sized 应该在我将类型参数绑定到 Foo 的任何地方使用。显然,这是不可能的...... :-(
    • @Pierre-Antoine 请注意,Tim 的第二个示例将 bar 更改为采用 T 而不是 &amp;T;因此,不需要 &amp;&amp;T 变为 &amp;[u32])。
    猜你喜欢
    • 1970-01-01
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 2021-08-03
    • 2011-04-13
    • 1970-01-01
    • 2014-05-08
    • 1970-01-01
    相关资源
    最近更新 更多