将长度为N的向量转换为长度为N/32的固定大小数组

5

编辑:自 Rust 版本>= 1.47 开始,此问题不再相关,因为已经开始实施 "const generics"。

我正在尝试用 Rust 实现数独求解器以进行学习。 我正在尝试创建一个由固定大小(81)的Cell数组(其中CellCopy)组成的棋盘,但似乎无法使其正常工作。 我可以创建一个包含9个Cell的线路,所以我想我遇到了这种转换类型的问题,对于这种转换,只有32个TryFrom泛型可用。

目前,Cell看起来像这样:

#[derive(Debug, Default, Clone, Copy)]
struct Cell {
    row: u8,
    column: u8,
}

这是有效的:

use std::convert::TryInto;
fn main() {
    let cells: Vec<Cell> = std::iter::repeat(0)
        .zip(0..9u8)
        .map(|(row, column)| Cell { row, column} )
        .collect();

    let c: Box<[Cell; 9]> = cells.into_boxed_slice().try_into().unwrap();

    println!("{:#?}", c);
}

但是这个不行:
use std::convert::TryInto;
fn main() {
    let cells: Vec<Cell> = (0..9u8).into_iter()
        .flat_map(|x| {
            std::iter::repeat(x)
                .zip(0..9u8)
        })
        .map(|(row, column)| Cell { row, column} )
        .collect();

    let c: Box<[Cell; 81]> = cells.into_boxed_slice().try_into().unwrap();

    println!("{:#?}", c);
}

我尝试像这样使用来自std的代码作为指南:

impl TryFrom<Box<[Cell]>> for Box<[Cell; 81]> {
    type Error = Box<[Cell]>;

    fn try_from(boxed_slice: Box<[Cell]>) -> Result<Self, Self::Error> {
        if boxed_slice.len() == 81 {
            Ok(unsafe { Box::from_raw(Box::into_raw(boxed_slice) as *mut [Cell; 91]) })
        } else {
            Err(boxed_slice)
        }
    }
}

但是这会遇到一个关于 trait 冲突实现 的错误,我想这很合理。

我知道我可以简单地使用一个 Vec 或者做一些像 [[Cell; 9]; 9] 这样的事情,但我真的想了解发生了什么。在尝试弄清楚这个问题的过程中,我看到了许多类似的问题,人们试图使用没有实现 Copy 的类型,那就是问题所在,但在这里并非如此,我无法弄清楚如何让它工作。


2
请注意,有几个拼写错误:第一个示例不起作用(没有针对装箱切片的try_into()),第二个示例也存在问题,尝试从迭代器构建[Cell; 81],第三个带有impl的块中有91而不是81等。 - Acorn
1个回答

4

对于slice,没有实现FromIterator trait。你可以将其封装在一个类型中并自行实现:

use core::iter::FromIterator;

#[derive(Debug, Default, Clone, Copy)]
struct Cell {
    row: u8,
    column: u8,
}

#[derive(Debug)]
struct Sudoku(Box<[Cell]>);

impl FromIterator<Cell> for Sudoku {
    fn from_iter<I: IntoIterator<Item=Cell>>(iter: I) -> Self {
        let mut v = Vec::new();
        for cell in iter {
            v.push(cell)
        }
        Sudoku(v.into_boxed_slice())
    }
}

fn main() {
    let cells: Sudoku = (0..9u8).into_iter()
        .flat_map(|x| {
            std::iter::repeat(x)
                .zip(0..9u8)
        })
        .map(|(row, column)| Cell { row, column} )
        .collect();


    println!("{:#?}", cells);
}

沙盒环境

编辑:

您还可以为特定大小的数组实现它。对于数独情况来说这应该是可以的,但通常你希望事情以更一般的方式工作。你可以编写一个宏,它能为任何给定大小写入实现代码。

示例:

use core::iter::FromIterator;
use std::fmt;

#[derive(Clone, Copy)]
struct Cell {
    row: u8,
    column: u8,
}

impl fmt::Display for Cell {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Cell: ({}, {})", self.row, self.column)
    }
}

struct Sudoku([Cell; 81]);

impl fmt::Display for Sudoku {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for cell in self.0.iter() {
            write!(f, "{}\n", cell)?;
        }
        Ok(())
    }
}


impl FromIterator<Cell> for Sudoku {
    fn from_iter<I: IntoIterator<Item=Cell>>(iter: I) -> Self {
        let mut v = [Cell {row: 0, column: 0}; 81];
        for (i, cell) in (0..81).zip(iter) {
            v[i] = cell;
        }
        Sudoku(v)
    }
}

fn main() {
    let cells: Sudoku = (0..9u8).into_iter()
        .flat_map(|x| {
            std::iter::repeat(x)
                .zip(0..9u8)
        })
        .map(|(row, column)| Cell { row, column} )
        .collect();


    println!("{}", cells);
}

Playground


这个代码可以运行,但是它并没有生成一个编译时大小的数组,而是生成了一个堆分配的 Box。我尝试修改你的示例代码,但是无法弄清楚其中的问题。Playground - xaocon
我已经让它像这样工作了。有更好的方法吗? - xaocon

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