1

I have a struct, which contains the following 2D array:

board: [[Option<Rc<dyn Piece>>; SIZE]; SIZE]

Incidentally, this is representing a chess board, and Piece is a trait, so if there is a better way to store this data, I would be interested.

I am having difficulty initializing this array. The obvious solution, setting everything to None:

board: [[None; SIZE]; SIZE]

doesn't work because

error[E0277]: the trait bound `std::rc::Rc<(dyn piece::Piece + 'static)>: std::marker::Copy` is not satisfied
  --> src/chessboard.rs:17:21
   |
17 |             board: [[None; SIZE]; SIZE]
   |                     ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::rc::Rc<(dyn piece::Piece + 'static)>`
   |
   = note: required because of the requirements on the impl of `std::marker::Copy` for `std::option::Option<std::rc::Rc<(dyn piece::Piece + 'static)>>`
   = note: the `Copy` trait is required because the repeated element will be copied

Some research led me to this github issue on the topic, https://github.com/rust-lang/rust/issues/54542, where there seems to be some disagreement about the topic, though most solutions appear to use MaybeUninit and unsafe rust to create the array in memory, iterate over it to initialize it, then either mem::transmute or into_inner it into the regular array. Because I am not very familiar with unsafe rust or dealing with memory, I'd rather not use these solutions, and am not entirely certain how to adapt those solutions, which are for [Vec<u8>; N] to my use case.

I found another article on the subject, https://www.joshmcguigan.com/blog/array-initialization-rust/, which presents a crate with a macro, arr! which is supposed to be entirely safe. However, I am also not certain if this is the most idiomatic and clean solution. Having to install an entire crate for such a small thing seems excessive (though that may be my feelings from languages, as I don't know much about the best practices in Rust).

Which, if either, of these solutions should I use, and if it is the former, how should I adapt it to arrays of arrays?

1 Answer 1

1

One way would be to use ndarray:

use ndarray::Array;
use std::sync::Arc;

trait Piece: std::fmt::Debug {}

#[derive(Debug)]
struct A {}

impl Piece for A {}

#[derive(Debug)]
struct B {}

impl Piece for B {}

fn main() {
    const SIZE: usize = 8;
    let a = Array::from_shape_fn((SIZE, SIZE), |i| {
        if (i.0 + i.1) % 2 == 0 {
            Some(Arc::new(A {}) as Arc<dyn Piece>)
        } else {
            Some(Arc::new(B {}) as Arc<dyn Piece>)
        }
    });

    println!("{:?}", a);
}

But that not very idiomatic in my opinion, I think you should prefer use an enum:

use ndarray::Array;
use std::sync::Arc;

trait Action {
    fn action(&self);
}

#[derive(Debug)]
struct A {}

impl Action for A {
    fn action(&self) {
        println!("Action of A");
    }
}

#[derive(Debug)]
struct B {}

impl Action for B {
    fn action(&self) {
        println!("Action of B");
    }
}

#[derive(Debug)]
enum Piece {
    A(A),
    B(B),
}

impl Action for Piece {
    fn action(&self) {
        match self {
            Piece::A(a) => a.action(),
            Piece::B(b) => b.action(),
        }
    }
}

fn main() {
    const SIZE: usize = 8;
    let a = Array::from_shape_fn((SIZE, SIZE), |i| {
        if (i.0 + i.1) % 2 == 0 {
            Some(Arc::new(Piece::A(A {})))
        } else {
            Some(Arc::new(Piece::B(B {})))
        }
    });

    println!("{:?}", a);
    for piece in a.iter().flatten() {
        piece.action();
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Yes, an enum makes a lot of sense. I didn't realize that would work like that. The reason I didn't go for an Rc<Option<dyn Piece>> was because I was getting the "size not defined error", as you can see in this playground: play.rust-lang.org/… However, with Piece being an enum, both forms appear to compile fine, so do you have any idea which is more efficient? I seem to recall that Option<Rc<T>> is optimized by storing None as zeros, and Some as any other pointer, though I'm not sure.
@RitobanRoyChowdhury I never use dyn trait nevermind, well the efficient question is a good one, I really don't know. docs.rs/enum_dispatch/0.1.3/enum_dispatch claim to speed up 10x by using enum. Yes Option<Rc<T>> will same some byte. I don't know why but in my mind the purpose was to stock the board not only the piece. In that maybe you should only have a hashmap of piece left in the board. where key is a tupple of their coordinate and just remove Option. There is not a only way to implement a chess game :p

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.