[英]Array of `Option<T>` to `Option` array
我正在尝试使用固定大小的 arrays。我想转换一个Option
值数组[Option<T>; N]
[Option<T>; N]
到Option<[T; N]>
Option<[T; N]>
如果所有条目都是Some
,我得到Some
,否则我得到None
。
Rust 通常使用迭代器来转换 collections,但这些没有长度保证。
这应该是合法的,因为如果存在,结果必须与参数的长度相同,但是有没有办法在不使用unwrap
或类似的情况下保留编译时长度保证的情况下做到这一点?
每晚,使用try_map()
(也许有一个稳定的板条箱支持它,我不知道):
#![feature(array_try_map)]
pub fn convert<T, const N: usize>(arr: [Option<T>; N]) -> Option<[T; N]> {
arr.try_map(|v| v)
}
在稳定的情况下,分配:
pub fn convert<T, const N: usize>(arr: [Option<T>; N]) -> Option<[T; N]> {
let arr = arr.into_iter().collect::<Option<Vec<T>>>()?;
Some(
arr.try_into()
.unwrap_or_else(|_| panic!("the array is of size {N}")),
)
}
在稳定的、非分配的情况下,但需要Default
并且可能效率低下:
pub fn convert<T: Default, const N: usize>(arr: [Option<T>; N]) -> Option<[T; N]> {
let mut result = [(); N].map(|()| T::default());
for (item, result_item) in std::iter::zip(arr, &mut result) {
*result_item = item?;
}
Some(result)
}
在稳定的,非分配的,使用不安全的。 应该是最后的手段。 下面的代码有点不正确,因为它不会在中间None
的情况下丢弃元素,这只是说明编写正确的不安全代码有多么困难:
use std::mem::{ManuallyDrop, MaybeUninit};
pub fn convert<T, const N: usize>(arr: [Option<T>; N]) -> Option<[T; N]> {
let arr = ManuallyDrop::new(arr);
let mut result_arr = MaybeUninit::<[T; N]>::uninit();
for (i, item) in arr.iter().enumerate() {
// SAFETY: This is in bounds, and wrapped in `ManuallyDrop` so not double drop.
unsafe {
result_arr
.as_mut_ptr()
.cast::<T>()
.add(i)
.write(std::ptr::read(item)?);
}
}
// SAFETY: We initialized it above.
Some(unsafe { result_arr.assume_init() })
}
您可以将.collect()
放入Option<Vec<T>>
和.and_then(|v| v.try_into().ok())
:
fn convert<T, const N: usize>(a: [Option<T>; N]) -> Option<[T; N]> {
a.into_iter().collect::<Option<Vec<_>>>().and_then(|a| a.try_into().ok())
}
这是直接和安全的,但不幸的是创建了一个中间Vec
因为T
和Option<T>
有不同的大小我认为你不能做得更好。 由于数组 ( [T;N]
) 上的迭代器总是产生N
项a.try_into().ok()
将总是导致Some
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.