使用范围或收集来初始化 Rust 数组。

10

我刚刚开始学习Rust,并尝试使用数组实现一些功能,但遇到了一些困难。第一个问题是初始化。我可以创建一个“空”的数组并遍历元素来填充它,但我如何从范围进行初始化呢?

let mut arr: [i32; 5] = [0; 5]; // this works
// let mut arr: [i32; 5] = 1..5 //this doesn't

第二个问题是尝试从 i32 数组上的简单映射初始化数组。这样做是不起作用的:
let mut arr2: [i32; 5] =  arr.iter().map(|&x| x + 1).collect();

实际上,即使是仅打印(而没有分配变量),也不起作用:

println!("{:?}", arr.iter().map(|&x| x + 1).collect());
// the above fails with: "type annotations needed: cannot infer type for type arameter `B` declared on the associated function `collect`"

你有什么智慧的话要分享吗?


1
如果数据源具有动态大小,则您可能需要使用vec而不是数组。 - Peter Hall
参见:https://stackoverflow.com/questions/67703525/initialize-array-of-arrays-from-iterator - Adam
2个回答

21

Rust 1.63 版本更新

使用 from_fn 方法:

let array: [usize; 5] = core::array::from_fn(|i| i + 1);
assert_eq!(array, [1, 2, 3, 4, 5]);

1
为什么每次都会增加 i 的值?我期望得到 [1, 1, 1, 1, 1]。我是 Rust 的新手。 - anilbey
4
@anilbey 因为这里的参数 i 是指索引而不是值。 - melMass

14
问题在于数组具有固定大小,编译时已知,但迭代器中的项数在编译时不可知即使它是一个 ExactSizeIterator,也没有 const 参数表示大小。因此,无法从迭代器到数组进行无误转换。
然而,有可失败的转换可用。最直接、常用的一种是 impl<T, A, const N: usize> TryFrom<Vec<T, A>> for [T; N],它允许你将 Vec<T> 转换为 [T; N],如果向量长度不正确则转换失败。
use std::convert::TryInto;

let arr: [i32; 5] = (1..=5).collect::<Vec<_>>()
    .try_into().expect("wrong size iterator");

这种方法的缺点是会为Vec进行临时堆分配并将其丢弃。如果您确实希望数组存储在堆上,则可能需要使用TryFrom<Box<[T], Global>> for Box<[T; N], Global>,它允许您从一个盒装切片中获取一个盒装数组。

let arr: Box<[i32; 5]> = (1..=5)
    .collect::<Box<[i32]>>()
    .try_into().expect("wrong size iterator");

如果您希望将数组分配给栈并避免临时分配,则必须使用std::iter::from_fn()进行操作,稍微有些麻烦:

let mut iter = 1..=5;
let arr: [i32; 5] = std::array::from_fn(|_| iter.next().expect("too short"));
assert!(iter.next().is_none(), "too long");

(如果您对长度有信心,那么之后就没有必要再进行断言,但内部的 expect/unwrap 是不可避免的。)

The second problem is trying to initialize an array from a simple map over an array on i32. This doesn't work:

let mut arr2: [i32; 5] =  arr.iter().map(|&x| x + 1).collect();
由于同样的原因,第一个方法也行不通:你目前不能直接收集到数组中。我上面提到的任何技巧都适用于这种情况。
自从这个问题和答案最初写出来以来,数组现在已经拥有了一个map方法,您可以使用它来完成此操作,而无需通过迭代器进行操作:
let mut arr2: [i32; 5] = arr.map(|x| x + 1);

实际上,这是一个不同于其他问题的问题。即使没有赋值,打印(printing)也不起作用: println!("{:?}", some_iterator.collect()) 总是会失败,因为你没有指定要收集到哪种类型中。collect() 从不假设收集类型,它总是需要在上下文中被指定或者推断出来。如果你指定了一个类型,那么你就可以打印迭代器的结果;原始输入是否为数组都是无关紧要的。
println!("{:?}", arr.iter().map(|&x| x + 1).collect::<Vec<_>>());

copy_from_slice可以使显式循环消失,但它仍然基本上是相同的原理。 - Bryan Larsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接