背景
今天在群里看到有人贴了一段报错代码:
pub trait VertexList<T> {
fn new() -> Self;
fn with_capacity(_cap: usize) -> Self {
Self::new() // here!
}
fn push(&mut self, v: T);
}
报错:
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> src/timer.rs:29:9
|
29 | Self::new()
| ^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `Self`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: the return type of a function must have a statically known size
help: consider further restricting `Self`
|
28 | fn with_capacity(_cap: usize) -> Self where Self: std::marker::Sized {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
编译器很贴心,告诉你该怎么改。编译器要给 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
pub trait VertexList<T>: Sized {
// ..
}
// 方法2
pub trait VertexList<T> {
fn with_capacity(_cap: usize) -> Self where Self: Sized{
Self::new()
}
}