Hello! I'm relatively new to Rust, and have mostly been working in C. I'd be happy to have some feedback on some code I wrote.
I wrote a program to verify Sudoku solutions. One of my favourite features of Rust is iterators, so my goal was to write a verifier that iterates over rows, columns, and squares, and verifies that each contains the numbers 1-9. Doing it this way, I can treat rows, column, and squares the same.
use std::collections::HashSet;
use std::io;
use std::io::prelude::*;
const WIDTH : usize = 9;
const GRID_SIZE : usize = WIDTH * WIDTH;
pub struct Sudoku {
grid: [i32; WIDTH * WIDTH]
}
impl Sudoku {
pub fn new() -> Sudoku {
Sudoku {
grid: [0; GRID_SIZE]
}
}
pub fn get_row_iter(&self, idx: usize) -> impl Iterator<Item = &i32> {
let slice = &self.grid[WIDTH * idx ..];
slice.iter().take(WIDTH)
}
pub fn get_col_iter(&self, idx: usize) -> impl Iterator<Item = &i32> {
let slice = &self.grid[idx..];
slice.iter().step_by(WIDTH)
}
pub fn get_square_iter(&self, idx: usize) -> impl Iterator<Item = &i32> {
let idx1 = (idx / 3) * 3 * WIDTH + (idx % 3) * 3;
let idx2 = idx1 + WIDTH;
let idx3 = idx2 + WIDTH;
let it1 = self.grid[idx1..].iter().take(3);
let it2 = self.grid[idx2..].iter().take(3);
let it3 = self.grid[idx3..].iter().take(3);
it1.chain(it2.chain(it3))
}
pub fn as_rows(&self) -> Vec<impl Iterator<Item = &i32>> {
let v : Vec<usize> = (0..WIDTH).collect();
v.iter().map( |&i| self.get_row_iter(i) ).collect()
}
pub fn as_cols(&self) -> Vec<impl Iterator<Item = &i32>> {
let v : Vec<usize> = (0..WIDTH).collect();
v.iter().map( |&i| self.get_col_iter(i) ).collect()
}
pub fn as_squares(&self) -> Vec<impl Iterator<Item = &i32>> {
let v : Vec<usize> = (0..WIDTH).collect();
v.iter().map( |&i| self.get_square_iter(i) ).collect()
}
fn is_section_complete<'a> (it : impl Iterator<Item = &'a i32>) -> bool {
static VALUES : [i32; WIDTH] = [1,2,3,4,5,6,7,8,9];
let set1 : HashSet<&i32> = VALUES.iter().collect();
let set2 : HashSet<&i32> = it.collect();
set1 == set2
}
pub fn is_solved(&self) -> bool {
for row in self.as_rows() {
if !Sudoku::is_section_complete(row) { return false }
}
for col in self.as_cols() {
if !Sudoku::is_section_complete(col) { return false }
}
for sqr in self.as_squares() {
if !Sudoku::is_section_complete(sqr) { return false }
}
true
}
}
One of my friends, who is a Python developer, suggested that I could simplify the solution part to iterate over types as well, as in
for as_section in (self.as_rows, self.as_cols, self.as_squares)
But I struggled with this, because the row, column, and square iterators have different types. I'd welcome any feedback, as well as ideas on how to implement iterating over types.