下面是一个我可以写下来的数据结构,它被Rust编译器接受:
pub struct Pair<S, T>(S, T);
pub enum List<T> {
Nil,
Cons(T, Box<List<Pair<i32, T>>>),
}
但是,我不能写
let x: List<i32> = List::Nil;
操场
因为Rust会抱怨"添加丢弃检查规则时出现溢出"。为什么不能实例化List::Nil
?
需要注意的是,以下工作:
pub struct Pair<S, T>(S, T);
pub enum List<T> {
Nil,
Cons(T, Box<List<T>>),
}
fn main() {
let x: List<i32> = List::Nil;
}
操场
当类型尚未实例化时,编译器主要担心类型的大小是常量,并且在编译时是已知的,因此可以将其放置在堆栈上。如果类型是无限的,Rust编译器会抱怨,通常Box
会通过创建对子节点的间接级别来解决这个问题,子节点的大小也是已知的,因为它也封装了自己的子节点。
不过,这不适合你的类型。
当您实例化List<T>
时,Cons
变体的第二个参数的类型是:
Box<List<Pair<i32, T>>>
请注意,内部List
有一个类型参数Pair<i32, T>
,而不是T
。
该内部列表还有一个Cons
,其第二个参数的类型为:
Box<List<Pair<i32, Pair<i32, T>>>>
它有一个Cons
,其第二个参数的类型为:
Box<List<Pair<i32, Pair<i32, Pair<i32, T>>>>>
等等。
现在,这并不能完全解释为什么你不能使用这种类型。该类型的大小将随着其在List
结构内的深度而线性增加。当列表很短(或为空(时,它不引用任何复杂的类型。
根据错误文本,溢出的原因与丢弃检查有关。编译器正在检查该类型是否被正确删除,如果它在进程中遇到另一个类型,它将检查该类型是否也被正确删除。问题是,每个连续的Cons
都包含一个全新的类型,它越深入就越大,编译器必须检查这些类型是否都会被正确删除。这个过程永远不会结束。