I’m learning Rust, (coming from Java) and decided to write a Chess Engine in Rust (as I previously have in Java) to get used to the language.
I’ve made structs for different types of chess moves (Slide, Passant, Castle, etc), each of which implements the Move trait. Here is an example:
pub struct PawnPush{
indices: [i8; 2],
befores: [i8; 2],
afters: [i8; 2],
passant_before: i8,
}
impl PawnPush{
pub fn new(from_index: i8, passant_square: i8) -> Self{
if from_index < 32{
Self{
indices: [from_index, from_index + 16],
befores: [piece::WP, piece::__],
afters: [piece::__, piece::WP],
passant_before : passant_square,
}
}
else{
Self{
indices: [from_index, from_index - 16],
befores: [piece::BP, piece::__],
afters: [piece::__, piece::BP],
passant_before : passant_square,
}
}
}
}
impl Move for PawnPush{
fn indices(&self) -> &[i8]{
return &(self.indices);
}
fn befores(&self) -> &[i8]{
return &(self.befores);
}
fn afters(&self) -> &[i8]{
return &(self.afters);
}
fn is_capture(&self) -> bool{
return false;
}
fn is_reversible(&self) -> bool{
return false;
}
fn passant_before(&self) -> i8{
return self.passant_before;
}
fn passant_after(&self) -> i8{
return if self.indices[0] < 32 {self.indices[0] + 8} else {self.indices[0] - 8};
}
fn get_name(&self) -> String{
return "".to_string();
}
fn moves_king(&self) -> bool{
return false;
}
}
The idea is that the Board can iterate through indices, befores, and afters of any move to implement them.
I’ve read through https://doc.rust-lang.org/book/ch17-02-trait-objects.html, and following that, I have some functions defined as such.
pub fn get_moves(&self) -> Vec<Box<dyn Move>>
pub fn take_move(&mut self, m : &impl Move)
The question is: How can I get an element from the vector returned from the first function to be given to the second function?
Like, if I do:
fn main() {
let mut b = Board::new(true);
let m = chess_move::PawnPush::new(12, -1);
b.take_move(&m);
b.print_board();
}
This works just fine.
Yet if I try something like:
fn main() {
let mut b = Board::new(true);
let m = b.get_moves()[0];
b.take_move(&m);
b.print_board();
}
I am met with a compilation error:
error[E0277]: the trait bound `Box<dyn Move>: Move` is not satisfied
--> board.rs:143:14
|
143 | b.take_move(&m);
| --------- ^^ the trait `Move` is not implemented for `Box<dyn Move>`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Move`:
Castle
KingMove
Passant
PawnPush
Slide
note: required by a bound in `Board::take_move`
--> board.rs:101:40
|
101 | pub fn take_move(&mut self, m : &impl Move){
| ^^^^ required by this bound in `Board::take_move`
Yet, I can still otherwise treat elements of b.get_moves() as if it did impl Move – I can call the methods from Move on m., such as:
fn main() {
let mut b = Board::new(true);
for m in b.get_moves(){
println!("{}",m.is_capture());
}
}
The Rust docs even have this example:
pub struct Screen {
pub components: Vec<Box<dyn Draw>>,
}
impl Screen {
pub fn run(&self) {
for component in self.components.iter() {
component.draw();
}
}
}
It seems strange to me that I can otherwise treat elements of b.get_moves() as if it did impl Move but I can’t give it’s reference to a function that wants a &impl Move. I’d greatly appreciate an explanation of what Box is/does, as I couldn’t really find much about it online.
Additionally, I’m also open to any suggestions if my entire approach is wrong and I shouldn’t be handling things in this way. Again, most of my programming experience is in Java, so maybe I’m trying to force OOP where it’s not needed.
>Solution :
b.get_moves() returns Vec<Box<dyn Move>>. Therefore, b.get_moves()[0] returns Box<dyn Move>. This type does not implement Move itself, because you haven’t written such implementation.
However, Box implements Deref, and as such can behave in many ways "as if" it was the inner type. The meaning of Deref is that you can use the * operator on it: for example, &*b.get_moves(0)[0] will give you &dyn Move, which is what you want. However, as Rust "auto-derefs" method calls, in other words, calls * as many times as needed automatically, you can call methods of Move directly on Box<dyn Move>, without the need to do (*v).method().