【问题标题】:How to convert a Vec into an array without copying the elements?如何在不复制元素的情况下将 Vec 转换为数组?
【发布时间】:2018-03-16 19:40:39
【问题描述】:

我有一个 Vec 的重要类型,其大小我确定。我需要将其转换为固定大小的数组。理想情况下,我想这样做

  1. 不复制数据 - 我想使用Vec
  2. 不使用零数据预初始化数组,因为这会浪费 CPU 周期

问题写成代码:

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let points = vec![
        Point { x: 1, y: 2 },
        Point { x: 3, y: 4 },
        Point { x: 5, y: 6 },
    ];

    // I would like this to be an array of points
    let array: [Point; 3] = ???;
}

这似乎是一个微不足道的问题,但是我无法在 Vec 文档、Rust Books 的切片部分或谷歌搜索中找到令人满意的解决方案。我发现的唯一一件事是首先用零数据初始化数组,然后从Vec 复制所有元素,但这不能满足我的要求。

【问题讨论】:

  • 不是 stackoverflow.com/questions/31360993/… 的副本,但它可以作为解决方案的一部分,尽管使用了 unsafe 关键字。也就是说,如果没有更简单更优雅的解决方案,我会感到惊讶
  • 如果没有动态长度 (alloca) 数组,它们本身就没那么有用,您通常会看到使用某种更高级别的结构 (github.com/servo/rust-smallvec ?)。当动态长度数组出现时,我希望出现更符合人体工程学的解决方案。
  • 让问题更具体:它应该支持特定长度的数组,还是任何长度的数组?为这个操作编写一个安全简单的接口显然不是一件容易的事。对于像[T; 3]这样的单个数组长度,会有简单的解决方案。
  • 是的,固定长度的数组是有意和想要的,而动态长度不是。在 API 中,固定长度数组指定了传递数据形状的协定。例如函数 create_triangle 将采用 3 个点的固定长度数组,因为三角形由 3 个点定义。它不能采用任何其他长度的点数组。所以我想可以说数组长度增加了关于使用的额外信息(这也意味着长度不需要在运行时验证)

标签: arrays vector rust


【解决方案1】:

正确地做到这一点非常困难。问题在于当存在部分未初始化的数组时正确处理恐慌。如果数组中的类型实现了Drop,那么它将访问未初始化的内存,导致未定义的行为。

最简单、最安全的方法是使用arrayvec

extern crate arrayvec;

use arrayvec::ArrayVec;

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let points = vec![
        Point { x: 1, y: 2 },
        Point { x: 3, y: 4 },
        Point { x: 5, y: 6 },
    ];

    let array: ArrayVec<_> = points.into_iter().collect();
    let array: [Point; 3] = array.into_inner().unwrap();
    println!("{:?}", array);
}

请注意,这只适用于 specific sizes of arrays,因为 Rust 还没有通用整数。 into_inner 也有一个你应该注意的性能警告。

另见:

【讨论】:

    【解决方案2】:

    还有try_into

    use std::convert::TryInto;
    
    #[derive(Debug)]
    struct Point {
        x: i32,
        y: i32,
    }
    
    fn main() {
        let v: Vec<Point> = vec![Point { x: 1, y: 1 }, Point { x: 2, y: 2 }];
        let a: &[Point; 2] = v[..].try_into().unwrap();
        println!("{:?}", a);
    }
    

    它不可变地借用,所以 Vec 不会被消耗。

    【讨论】:

      【解决方案3】:

      只是为了好玩,这里有一些例子表明安全的 Rust 为我们提供了针对特定小尺寸的方法,例如:

      /// Return the array inside Some(_), or None if there were too few elements
      pub fn take_array3<T>(v: &mut Vec<T>) -> Option<[T; 3]> {
          let mut iter = v.drain(..);
          if let (Some(x), Some(y), Some(z))
              = (iter.next(), iter.next(), iter.next())
          {
              return Some([x, y, z]);
          }
          None
      }
      
      /// Convert a Vec of length 3 to an array.
      ///
      /// Panics if the Vec is not of the exact required length
      pub fn into_array3<T>(mut v: Vec<T>) -> [T; 3] {
          assert_eq!(v.len(), 3);
          let z = v.remove(2);
          let y = v.remove(1);
          let x = v.remove(0);
          [x, y, z]
      }
      

      拥有Vec 的基本方法是:removepopdraininto_iter 等。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-14
        • 2019-03-03
        • 1970-01-01
        • 1970-01-01
        • 2013-05-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多