Please help me condense this code

This code works but it feels like there is a lot of repetition and I am unsure of how to properly share functionality.

The basic need is to have an 8x8 matrix and a 10x10 matrix on the stack. Would be nice to be generic than that.
The dimensions of the array are not going to change but I don't know how to declare a generic structure where I can define the size through implementation so that the compiler understands it is a static size.
In both cases, you need to get and set cells at a row/col. The get and set functions do exactly the same thing so it feels like you should be able to define a default implementation for those except for the fact that it would require a convention that a data property exists on the structure implementing the trait.

Would be nice if you could specify a condition on the trait to say: "You can only implement this trait on a struct that has a property with this name and type".

What would be the best way to declare something like this without all this repetition?

pub trait GameBoard<T> {
    fn size(&self) -> usize;
    fn get(&self, row: usize, col: usize) -> &T;
    fn set(&mut self, row: usize, col: usize, value: T);
}

pub struct Board8X<T> {
    data: [[T;8]; 8]
}

impl<T: std::marker::Copy> Board8X<T> {
    pub fn new(default: T) -> Board8X<T> {
        Board8X {
            data: [[default; 8]; 8]
        }
    }
}

impl<T> GameBoard<T> for Board8X<T> {
    fn size(&self) -> usize {
        8
    }

    fn get(&self, row: usize, col: usize) -> &T {
        &self.data[row][col]
    }

    fn set(&mut self, row: usize, col: usize, value: T) {
        self.data[row][col] = value;
    }
}

pub struct Board10X<T> {
    data: [[T;10]; 10]
}

impl<T: std::marker::Copy> Board10X<T> {
    pub fn new(default: T) -> Board10X<T> {
        Board10X {
            data: [[default; 10]; 10]
        }
    }
}

impl<T> GameBoard<T> for Board10X<T> {
    fn size(&self) -> usize {
        10
    }

    fn get(&self, row: usize, col: usize) -> &T {
        &self.data[row][col]
    }

    fn set(&mut self, row: usize, col: usize, value: T) {
        self.data[row][col] = value;
    }
}

You can use const generics for this. It would look something like this

playground

pub trait GameBoard<T> {
    fn size(&self) -> usize;
    fn get(&self, row: usize, col: usize) -> &T;
    fn set(&mut self, row: usize, col: usize, value: T);
}

pub struct Board<T, const SIZE: usize> {
    data: [[T;SIZE]; SIZE]
}

type Board8X<T> = Board<T,8>;
type Board10X<T> = Board<T,10>;


impl<T: Copy, const SIZE: usize> Board<T, SIZE> {
    pub fn new(default_value: T) -> Board<T, SIZE> {
        Board {
            data: [[default_value; SIZE]; SIZE]
        }
    }
}

impl<T, const SIZE: usize> GameBoard<T> for Board<T,SIZE> {
    fn size(&self) -> usize {
        SIZE
    }

    fn get(&self, row: usize, col: usize) -> &T {
        &self.data[row][col]
    }

    fn set(&mut self, row: usize, col: usize, value: T) {
        self.data[row][col] = value;
    }
}
5 Likes

Aaaah that looks much better, thanks to a bunch, will give that a try.

So I tried it and it was exactly what I was looking for, thank you very much

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.