我是否可以声明给定特征的实现不应该是大小的?

时间:2018-05-23 08:18:04

标签: rust traits

我定义了一个特征Foo,为[u32]实现了这个特征,并编写了一个函数bar,将这个特征作为参数(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),否则编译器仍会隐式地假设它。对于这个特殊的特征,我想改变的是这个隐含的假设。

1 个答案:

答案 0 :(得分:2)

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

  

当你说Foo SizedFoo时,你就会把真相隐藏起来。是的,每个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]`

T

有关详细信息,请参阅this answerTrait implementing Sized

为什么不能{\}} Sized不合格,因为我们只有参考?

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

  

默认情况下,函数上的所有泛型类型隐式绑定?Sized,无论它们如何使用。您需要使用fn bar<T>(f: &T) -> u32 where T: Foo + ?Sized, { f.foo() + 1 }

明确选择退出该要求

这将解决您的问题:

Foo

Playground

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

Foo

Playground

在这种特殊情况下,您甚至可以概括Vec的实现,它可以用于impl<T: AsRef<[u32]>> Foo for T { fn foo(&self) -> u32 { self.as_ref()[0] } } 和数组以及对这些类型的引用:

bar

使用新的impl Trait语法,fn bar(f: impl Foo) -> u32 { f.foo() + 1 } 可以在最后两种情况下缩短为

\t

Playground