在安全的Rust中,编译器要求数组一旦被声明,它所占用的内存应当被完全初始化。但是,在一些情况下,这样会导致没法很灵活的对数组进行默认初始化。
问题
请看这个例子:
对于这样一个结构体,我们要初始化[Option<File>; FileDescriptorVec::PROCESS_MAX_FD]这个数组,想要把它的每个元素默认初始化为None。并且,由于其它的原因,我们不能够为File结构体实现Copy Trait.
如果我们使用这样的方式来把数组初始化为None:
就会报错:
报错的原因是,File结构体未实现Copy Trait,导致我们用None对Option<T>进行默认初始化的时候,编译器无法直接把Option<T>复制到数组的每个元素之中。
上文说到,由于其他原因的限制,我们不能为File实现Copy这个trait,因此,我们需要找别的方法,初始化这个数组。
解决方案:MaybeUninit
不安全的 Rust 给了我们一个强大的工具来处理这个问题:MaybeUninit
。这个类型可以用来处理还没有完全初始化的内存。通过使用MaybeUninit
,我们可以对一个数组进行逐个元素的初始化。
首先,我们声明一个MaybeUninit的类型的数组:
这个数组的元素类型就是MaybeUninit<Option<File>>,并且,在代码里面,我们通过assume_init()声称已经完全初始化了它。这听着不靠谱,但是,MaybeUninit本身就不需要初始化,因此,我们假设它已经初始化,是没有问题的。但是,请注意,这样会产生一个无效的类型实例(因为Option<File>实际上没有被初始化),并且会带来一些未定义的行为。
接着,我们在一个循环里面,初始化这个数组:
有的同学可能会疑惑:这样赋值不就导致了原先的”MaybeUninit”类型上面产生了一个drop了吗?
答案就是,MaybeUninit的类型,它的Drop Trait,产生的动作就是:“什么也不做”。也就是说,不会调用内层的类型的Drop方法。
在上面这段代码过后,整个数组都被初始化为None了,一切准备就绪,我们使用以下代码,把“未初始化”的类型,强制转换为“已经初始化”的类型:
于是,我们就能用这个data,去初始化FileDescriptorVec结构体了!