rust - 我可以声明给定特征的实现不应该被调整大小吗?

标签 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) >)。对于这个特定的特征,我想改变的是这个隐含的假设。

最佳答案

为什么编译器会提示trait Foo: ?Sized

When you say that every Foo is Sized, you're kind of hiding the truth to yourself. Yes, every Foo is Sized but actually every type has a given size at some point. The real important information is that you're not saying how much this size is.

在这种情况下,您需要一个尺寸的[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?

By default, all generic types on functions implicitly have the Sized bound, regardless of how they are used. You need to explicitly opt-out of that requirement using ?Sized

这将解决您的问题:

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

Playground

也可以为 &[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 的实现,它适用于 Vec 和数组以及对这些类型的引用:

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

关于rust - 我可以声明给定特征的实现不应该被调整大小吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50483245/

相关文章:

c++ - 我可以在 Rust 中就地构建吗?

rust - 如何在 Rust 中定义一个符合 trait 的变量?

memory - 如何调试 Rust 中的内存问题?

struct - 如何使用特征的不同实现实例化相同的结构?

traits - 性状中的混合角色显然不起作用

c++ - boost add_reference 不适用于模板参数

casting - 如何将 Vec<Box<dyn Trait>> 移动到 Vec<Rc<RefCell<dyn Trait>>>

rust - 展开选项类型出现错误 : cannot move out of `*foo` which is behind a shared reference

rust - 在 for 循环中使用拥有的指针访问枚举的内容

rust - 如何实现 ToString 特征以创建没有尾随逗号的逗号分隔字符串?