【问题标题】:How do I collect into an array?如何收集到数组中?
【发布时间】:2014-11-05 12:35:16
【问题描述】:

我想在枚举数组上调用.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]>();
}

但编译器抱怨:

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]`

我该怎么做?

【问题讨论】:

    标签: rust


    【解决方案1】:

    问题实际上出在collect,而不是map

    为了能够将迭代的结果收集到一个容器中,这个容器应该实现FromIterator

    [T; n] 没有实现FromIterator,因为它通常不能这样做:要生成[T; n],您需要准确提供n 元素,但是在使用FromIterator 时,您不能保证元素的数量这将被输入到您的类型中。

    还有一个困难,如果没有补充数据,您现在应该输入数组的哪个索引(以及它是空的还是满的)等等......这可以通过使用enumerate 来解决在map 之后(基本上是提供索引),但是如果提供的元素不足或过多,您仍然会遇到决定做什么的问题。

    因此,不仅目前无法在固定大小的数组上实现FromIterator;但即使在未来,这似乎也是一个遥远的目标。


    那么,现在该怎么办?有几种可能:

    • 在调用站点内联转换:[Value(1), Value(2), Value(3)],可能借助宏
    • 收集到不同的(可增长的)容器中,例如Vec&lt;Foo&gt;
    • ...

    【讨论】:

    • 当您说“在没有补充数据的情况下很难 [to] 知道您现在应该输入数组的哪个索引”时,我不明白您的意思。
    • 虽然不可能为数组实现FromIterator,但标准库中可能存在类似的函数来返回Result&lt;[T; n], E&gt;。甚至可以收集到那种类型。
    • 对于 ExactSizeIterator 迭代器,在使用元素之前进行检查很简单。
    【解决方案2】:

    虽然由于其他答案所述的原因,您无法直接收集到数组中,但这并不意味着您不能收集到由数组支持的数据结构中,例如 @ 987654321@:

    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"));
        // use `.expect` instead if your type implements `Debug`
    }
    

    ArrayVec 中拉出数组会返回一个Result,以处理没有足够的项目来填充它的情况;其他答案中讨论的情况。

    【讨论】:

    • 啊,我想知道ArrayVec 是否支持这个!谢谢。
    • 我错过了为Foo 复制#[derive(Debug)],但没关系。
    • @SvenMarnach 啊。我只是根据OP中的代码来做的。添加了一条评论,尽可能将人们指向.expect
    【解决方案3】:

    更新

    这可以工作:

    let array: [T; N]  = something_iterable.[into_]iter()
        .collect::<Vec<T>>()
        .try_into()
        .unwrap()
    

    在较新版本的 rust 中,try_into 包含在前奏中,因此不需要use std::convert::TryInto。此外,从1.48.0开始,数组支持直接从Vec类型转换,签名来自stdlib源:

    fn try_from(mut vec: Vec<T, A>) -> Result<[T; N], Vec<T, A>> {
        ...
    }
    

    原答案

    从 rustc 1.42.0 开始,如果您的元素 impl Copy 特征,为简单起见,这只是工作:

    use std::convert::TryInto;
    
    ...
    
    let array: [T; N]  = something_iterable.[into_]iter()
        .collect::<Vec<T>>()
        .as_slice()
        .try_into()
        .unwrap()
    
                  collect            as_slice          try_into + unwrap()
    Iterator<T>   ------>   Vec<T>   ------->   &[T]   ------------------>   [T]
    

    但我只是将其称为一种解决方法。 您需要包含 std::convert::TryInto,因为 try_into 方法是在 TryInto 特征中定义的。


    以下是您调用try_into 时检查的签名,取自source。如您所见,这需要您的类型 T 实现 Copy 特征,因此理论上,它将复制您的所有元素一次。

    #[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)
        }
    }
    

    【讨论】:

    • 不工作,我收到此错误rust | 138 | .try_into() | ^^^^^^^^ the trait `std::convert::TryFrom&lt;&amp;[i32]&gt;` is not implemented for `[i32; 64]` | = help: the following implementations were found: &lt;&amp;'a [T; N] as std::convert::TryFrom&lt;&amp;'a [T]&gt;&gt; &lt;&amp;'a mut [T; N] as std::convert::TryFrom&lt;&amp;'a mut [T]&gt;&gt; &lt;[T; N] as std::convert::TryFrom&lt;&amp;[T]&gt;&gt; = note: required because of the requirements on the impl of `std::convert::TryInto&lt;[i32; 64]&gt;` for `&amp;[i32]`
    • 明确的.as_slice()(和Copy约束)在1.48.0中是unnecessary
    • try_into() 方法的 Playground 链接:play.rust-lang.org/…
    • 通过Vec&lt;T&gt; 的间接访问在某种程度上违背了目的。为了避免动态分配,通常需要收集到数组中。
    【解决方案4】:

    在这种情况下你可以使用Vec&lt;Foo&gt;:

    #[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);
    }
    

    【讨论】:

      【解决方案5】:

      在夜间使用 array_map 功能标志,您可以执行以下操作:

      #![feature(array_map)]
      fn main() {
          let bar = [1, 2, 3];
          let foos = bar.map(|x| Foo::Value(x));
      }
      

      【讨论】:

      【解决方案6】:

      .collect() 构建可以具有任意长度的数据结构,因为迭代器的项号通常不受限制。 (Shepmaster 的回答已经提供了很多细节)。

      在不分配Vec 或类似的情况下,将数据从映射链中获取到数组中的一种可能性是将数组的可变引用引入链中。在您的示例中,看起来像这样:

      #[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);
      }
      

      .zip() 使迭代以锁步方式运行在barfoos 上——如果foos 分配不足,则根本不会映射更高的bars,如果它是过度分配,它将保留其原始初始化值。 (因此还有 Clone 和 Copy,它们是 [Nothing; 3] 初始化所必需的)。

      【讨论】:

        【解决方案7】:

        这是不可能的,因为数组没有实现任何特征。您只能收集实现 FromIterator 特征的类型(请参阅 its docs 底部的列表)。

        这是一个语言限制,因为目前不可能对数组的长度进行泛型,并且长度是其类型的一部分。但是,即使它可能的,FromIterator 也不太可能在数组上实现,因为如果产生的项目数不完全是数组的长度,它就不得不恐慌。

        【讨论】:

        • 数组确实实现了特征,至少是短的。标准库包含许多短数组的实现(我认为最多 12 个元素)。问题是你不能为所有数组做一个通用的实现,而且没有一个数组实现 FromIterator。
        【解决方案8】:

        您实际上可以定义一个 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]
        }
        

        这是playground link

        【讨论】:

          【解决方案9】:

          我对我的阶乘进行了硬编码,因为它们增长得非常快:

          const N: usize = 7;
          const FACTORIAL: usize = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800][N];
          type Perm = [u8; N];
          type Perms = [Perm;FACTORIAL];
          

          【讨论】:

            【解决方案10】:

            我自己也遇到过这个问题——这里有一个解决方法。

            您不能使用FromIterator,但您可以遍历固定大小对象的内容,或者,如果事情更复杂,则可以对可以访问的任何内容进行切片的索引。无论哪种方式,突变都是可行的。

            例如,我遇到的问题是[[usize; 2]; 4] 类型的数组:

            fn main() {
                // Some input that could come from another function and thus not be mutable
                let pairs: [[usize; 2]; 4] = [[0, 0], [0, 1], [1, 1], [1, 0]];
            
                // Copy mutable
                let mut foo_pairs = pairs.clone();
            
                for pair in foo_pairs.iter_mut() {
                    // Do some operation or other on the fixed-size contents of each
                    pair[0] += 1;
                    pair[1] -= 1;
                }
                // Go forth and foo the foo_pairs
            }
            

            如果这发生在一个小函数中,在我的书中没关系。无论哪种方式,您最终都会得到与相同类型相同类型的转换值,因此首先复制整个事物然后进行变异与在闭包中引用一个值并返回它的某些函数的工作量大致相同.

            请注意,这仅适用于您计划计算将是相同类型的东西,包括大小/长度。但这是您使用 Rust 数组所暗示的。 (具体来说,您可以随意Value()Foos 或Nothing 它们,并且仍然在您的数组的类型参数内。)

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-05-31
              • 1970-01-01
              • 1970-01-01
              • 2020-03-11
              • 1970-01-01
              • 1970-01-01
              • 2022-11-19
              相关资源
              最近更新 更多