为什么由于"overflow while adding drop-check rules"而无法实例化数据结构?



下面是一个我可以写下来的数据结构,它被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都包含一个全新的类型,它越深入就越大,编译器必须检查这些类型是否都会被正确删除。这个过程永远不会结束。

最新更新