Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to pass a function which implements a trait with Box in Rust

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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().

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading