Converting a vector of size N to a fixed size array length N over 32

Viewed 667

EDIT: This question isn't relevant on versions of Rust >= 1.47 because "const generics" has started being implemented.

I'm trying to implement a sudoku solver in Rust for learning purposes. I'm trying to create a board with a fixed size (81) array of Cells (which is Copy) but I can't seem to get it to work. I can create a line of 9 Cells so I guess I'm running into the problem where there are only generics for TryFrom for up 32 for this kind of conversion.

Cell looks like this for now:

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

This works:

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

But this doesn't:

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

I tried to use the code from std as a guide like this:

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

but that runs into an error about conflicting implementations of trait which I guess makes sense.

I know I could just use a Vec or do something like [[Cell; 9]; 9] but I'd really like to understand what's going on. While trying to figure this out I've seen a number of similar questions where people were trying to use types that didn't implement Copy and that was the problem but that's not the case here and I can't figure out how to make this work.

1 Answers

The trait FromIterator is not implemented for slices, you can wrap it in a type and implement it yourself:

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

Playground

Edit:

You can also implement it for the specific size of the array. Which for the sudoku case should be ok, but in general you would like things to work in a more general way. Instead of the fixated size, you could write a macro that would write the implementation for any given size too.

Example:

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

Related