背景

今天在群里看到有人贴了一段报错代码:

  1. pub trait VertexList<T> {
  2. fn new() -> Self;
  3. fn with_capacity(_cap: usize) -> Self {
  4. Self::new() // here!
  5. }
  6. fn push(&mut self, v: T);
  7. }

报错:

  1. error[E0277]: the size for values of type `Self` cannot be known at compilation time
  2. --> src/timer.rs:29:9
  3. |
  4. 29 | Self::new()
  5. | ^^^^^^^^^ doesn't have a size known at compile-time
  6. |
  7. = help: the trait `std::marker::Sized` is not implemented for `Self`
  8. = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
  9. = note: the return type of a function must have a statically known size
  10. help: consider further restricting `Self`
  11. |
  12. 28 | fn with_capacity(_cap: usize) -> Self where Self: std::marker::Sized {
  13. | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

编译器很贴心,告诉你该怎么改。编译器要给 with_capacity() 的默认实现分配栈空间(new方法没有默认实现,因此这里没有报错),需要在编译期确定Self::new()的大小,而且这个大小不能变化,也就是这里的返回值满足 Sized

Sized

Sized 是一个自动实现的trait, 表示类型在编译期可以确定一个常量长度,如:类型 &u8, [u8; 10], Vec<[u8]> 都是 Sized , 但 str, [u8]?Sized

它有如下特点:

  • 类型参数T 默认是 T: Sized 。如果要表示允许固定或者非固定长度类型,要显式写成 T: ?Sized
  • trait的定义中有一个隐含的 Self 类型,指代实现该trait的类型(在声明时不确定是哪个类型)。此处的Self 默认是 Self: ?Sized ,这与上一条的恰好相反。所以在例子中,需要手动添加 Where Self: Sized 。Rust认为所有size的类型都可以实现trait,因此从语义上默认trait的Self的长度不一定是常量。
  • 因为语义上排除了trait的Self不是Sized,我们可以在trait定义中复用 Self:Sized 来方法加入trait obj的vtable, 或者禁止trait生成trait object。

解决办法

  1. // 方法1
  2. pub trait VertexList<T>: Sized {
  3. // ..
  4. }
  5. // 方法2
  6. pub trait VertexList<T> {
  7. fn with_capacity(_cap: usize) -> Self where Self: Sized{
  8. Self::new()
  9. }
  10. }

参考