我希望在 Rust 中获取已知数量的输入。在本例中,我正在考虑欧拉项目第 54 题的扑克手牌,其中每行解析为两手牌,每手牌五张。对于这种情况,我发现有两种主要方法,但我都不喜欢。
方法一:
let mut buffer_1 = Vec::with_capacity(5);
for i in 0..5 {
buffer_1.push(i)
}
assert_eq!(buffer_1.len(), 5);
方法二:
let mut buffer_2 = [None, 5];
for i in 0..5 {
buffer_2[i as usize] = Some(i)
}
尽管在编译时大小已知,但方法 1 位于堆上,而方法 2 为我提供了一个可选值,其中我知道所有内容都是Some
. 我理想的情况是能够将某些函数或集合收集到数组或类似的数组中。例如
fn array_from_colsure<T>(length: usize, closure: fn() -> T) -> Option<[T; length]> {
// implementation
}
#[test]
fn array_from_closure_test() {
let a: [i32; 5] = array_from_colsure(5, || {for i in 0..5 {i}}).unwrap()
}
澄清: 我希望找到具有此功能的东西,而不是从头开始创建它。
正如 Silvio Mayolo 的出色回答所示(自最初的问题以来,我对所需的功能进行了轻微的编辑),实现我的建议将需要大量的代码unsafe
(更不用说为如此小的优化付出巨大的努力)。所以对于少数项目来说这样做是不明智的。
您可以使用
std::array::from_fn
:我还提供了
ArrayVec
arrayvec或tinyvec板条箱,这看起来像是您的第一种方法,但不会使用堆分配。首先,我想说的是,你在这里所做的事情是过早优化的。您试图以编写可读、可维护的代码为代价来节省微秒。方法 1 具有正确的类型,从不重新分配,并且对于人类读者来说是显而易见的,因此您绝对应该使用方法 1。
然而,让我们假设我们使用的是微处理器。我们在医院的一些嵌入式机器上运行,每个字节都很重要,并且堆分配存在问题。
MaybeUninit
可用于分配堆栈空间而不在其中存储任何内容。这是一个相当令人困惑的说法。我们正在创建一些未初始化的内存,然后断言它已初始化。但事实并非如此:它尚未初始化。好吧,真正的
assume_init
意思是“该内存已根据类型所需进行了初始化”,并且由于数组仅包含,因此该类型未初始化就可以了。MaybeUninit
现在我们写入一些数据。我假设你的实际计算比 更复杂
i as i32
,但对于这个例子来说它会做得很好。现在我们得到了一个可能未初始化的值的数组,我们知道这些值已经被初始化。由于
MaybeUninit<T>
和T
具有相同的内存表示形式,因此我们可以将其中一种转换为另一种。transmute
没有运行时开销,因为它实际上只是将一种类型的字节重新解释为另一种类型的字节。这是我们的整个代码块。这里的数量
unsafe
应该会吓到你。它应该让你跑去叫 Rust 警察。我们通常不以这种方式编写代码是有原因的。但如果您发现自己处于百万分之一的情况,即额外的少量字节确实值得这么复杂,那么这是可以做到的。