这是一个我可以写下来的数据结构,它被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会抱怨“添加drop-check规则时出现溢出”。为什么不能实例化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
都包含一个全新的类型,它越深越大,编译器必须检查是否每个都将被正确删除。这个过程永远不会结束。