我想调用.map()一系列枚举:
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let foos = bar.iter().map(|x| Foo::Value(*x)).collect::<[Foo; 3]>();
}
Run Code Online (Sandbox Code Playgroud)
但编译器抱怨:
error[E0277]: the trait bound `[Foo; 3]: std::iter::FromIterator<Foo>` is not satisfied
--> src/main.rs:8:51
|
8 | let foos = bar.iter().map(|x| Foo::Value(*x)).collect::<[Foo; 3]>();
| ^^^^^^^ a collection of type `[Foo; 3]` cannot be built from an iterator over elements of type `Foo`
|
= help: the trait `std::iter::FromIterator<Foo>` is not implemented for `[Foo; 3]`
Run Code Online (Sandbox Code Playgroud)
我该怎么做呢?
Jea*_*ant 28
对于您的具体问题,Rust 1.55.0 允许您直接映射数组:
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let foos = bar.map(Foo::Value);
}
Run Code Online (Sandbox Code Playgroud)
Mat*_* M. 23
问题实际上是在collect,而不是在map.
为了能够将迭代的结果收集到容器中,该容器应该实现FromIterator.
[T; n]没有实现,FromIterator因为它通常不能这样做:生成一个[T; n]你需要n准确提供元素,但是在使用时FromIterator你不能保证将被输入你的类型的元素数量.
如果没有补充数据,您现在还应该知道阵列的哪个索引(以及它是空的还是满的)等等,这也很难以解决...这可以通过使用enumerateafter map(基本上为索引提供)来解决),但是如果没有提供足够的或过多的元素,你仍然会有决定做什么的问题.
因此,不仅在目前无法FromIterator在固定大小的阵列上实现; 但即使在未来,它似乎是一个长镜头.
那么,现在该怎么办?有几种可能性:
[Value(1), Value(2), Value(3)]可能在宏的帮助下Vec<Foo>She*_*ter 13
虽然由于其他答案所述的原因,您不能直接收集到数组中,但这并不意味着您不能收集到由数组支持的数据结构中,例如ArrayVec:
use arrayvec::ArrayVec; // 0.7.0
use std::array;
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let foos: ArrayVec<_, 3> = array::IntoIter::new(bar).map(Foo::Value).collect();
let the_array = foos
.into_inner()
.unwrap_or_else(|_| panic!("Array was not completely filled"));
}
Run Code Online (Sandbox Code Playgroud)
将数组从ArrayVec返回 a 中拉出来Result以处理没有足够项来填充它的情况;在其他答案中讨论的案例。
在这种情况下,您可以使用Vec<Foo>:
#[derive(Debug)]
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let foos = bar.iter().map(|&x| Foo::Value(x)).collect::<Vec<Foo>>();
println!("{:?}", foos);
}
Run Code Online (Sandbox Code Playgroud)
小智 6
.collect()构建可以具有任意长度的数据结构,因为迭代器的项目编号通常不受限制。(Shepmaster 的回答已经在那里提供了很多细节)。
在不分配 aVec或类似值的情况下将数据从映射链获取到数组中的一种可能性是将对该数组的可变引用引入链中。在您的示例中,它看起来像这样:
#[derive(Debug, Clone, Copy)]
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let mut foos = [Foo::Nothing; 3];
bar.iter().map(|x| Foo::Value(*x))
.zip(foos.iter_mut()).for_each(|(b, df)| *df = b);
}
Run Code Online (Sandbox Code Playgroud)
这.zip()使得迭代在两个bar和foos锁步中运行——如果foos分配不足,则bar根本不会映射较高的s,如果分配过多,它将保留其原始初始化值。(因此也是克隆和复制,它们是[Nothing; 3]初始化所必需的)。
您可以将数组map方法与Iterator::next.
例子:
fn iter_to_array<Element, const N: usize>(mut iter: impl Iterator<Item = Element>) -> [Element; N] {
// Here I use `()` to make array zero-sized -> no real use in runtime.
// `map` creates new array, which we fill by values of iterator.
let res = [(); N].map(|_| iter.next().unwrap());
// Ensure that iterator finished
assert!(matches!(iter.next(), None));
res
}
Run Code Online (Sandbox Code Playgroud)
UPD
Rust 1.63 发布后,可以使用std::array::from_fn以下方法:
fn iter_to_array<Element, const N: usize>(mut iter: impl Iterator<Item = Element>) -> [Element; N] {
// Here I use `()` to make array zero-sized -> no real use in runtime.
// `map` creates new array, which we fill by values of iterator.
let res: [_; N] = std::array::from_fn(|_| iter.next().unwrap());
// Ensure that iterator finished
assert!(matches!(iter.next(), None));
res
}
Run Code Online (Sandbox Code Playgroud)
小智 5
您实际上可以定义一个Iterator特征扩展来做到这一点!
use std::convert::AsMut;
use std::default::Default;
trait CastExt<T, U: Default + AsMut<[T]>>: Sized + Iterator<Item = T> {
fn cast(mut self) -> U {
let mut out: U = U::default();
let arr: &mut [T] = out.as_mut();
for i in 0..arr.len() {
match self.next() {
None => panic!("Array was not filled"),
Some(v) => arr[i] = v,
}
}
assert!(self.next().is_none(), "Array was overfilled");
out
}
}
impl<T, U: Iterator<Item = T>, V: Default + AsMut<[T]>> CastExt<T, V> for U { }
fn main () {
let a: [i32; 8] = (0..8).map(|i| i * 2).cast();
println!("{:?}", a); // -> [0, 2, 4, 6, 8, 10, 12, 14]
}
Run Code Online (Sandbox Code Playgroud)
这是一个游乐场链接。
从 rustc 1.42.0 开始,如果您的元素具有 implCopy特征,为简单起见,这仅适用:
use std::convert::TryInto;
...
let array: [T; N] = something_iterable.[into_]iter()
.collect::<Vec<T>>()
.as_slice()
.try_into()
.unwrap()
Run Code Online (Sandbox Code Playgroud)
collect as_slice try_into + unwrap()
Iterator<T> ------> Vec<T> -------> &[T] ------------------> [T]
Run Code Online (Sandbox Code Playgroud)
但我只是称之为一种解决方法。您需要包含,std::convert::TryInto因为该try_into方法是在TryInto特征中定义的。
以下是您在调用时检查的签名try_into,取自source。如您所见,这需要您的类型T实现Copytrait,因此理论上,它将复制您的所有元素一次。
#[stable(feature = "try_from", since = "1.34.0")]
impl<T, const N: usize> TryFrom<&[T]> for [T; N]
where
T: Copy,
[T; N]: LengthAtMost32,
{
type Error = TryFromSliceError;
fn try_from(slice: &[T]) -> Result<[T; N], TryFromSliceError> {
<&Self>::try_from(slice).map(|r| *r)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
17576 次 |
| 最近记录: |