5
votes

I am just learning Rust. I am trying to create a builder struct for my Game struct. Here is the code:

struct Input {
    keys_pressed: HashMap<VirtualKeyCode, bool>,
}

pub struct GameBuilder {
    settings: GameSettings,
    input: Input,
}

impl GameBuilder {
    pub fn new() -> GameBuilder {
        GameBuilder {
            settings: GameSettings {
                window_dimensions: (800, 600),
                title: "".to_string(),
            },
            input: Input {
                keys_pressed: HashMap::new(),
            }
        }
    }

    pub fn with_dimensions(&mut self, width: u32, height: u32) -> &mut GameBuilder {
        self.settings.window_dimensions = (width, height);
        self
    }

    pub fn with_title(&mut self, title: &str) -> &mut GameBuilder {
        self.settings.title = title.to_string();
        self
    }

    pub fn game_keys(&mut self, keys: Vec<VirtualKeyCode>) -> &mut GameBuilder {
        for key in keys {
            self.input.keys_pressed.insert(key, false);
        }
        self
    }

    pub fn build(&self) -> Game {
        let (width, height) = self.settings.window_dimensions;
        Game {
            display: glutin::WindowBuilder::new()
                        .with_dimensions(width, height)
                        .with_title(self.settings.title.to_string())
                        .build_glium()
                        .ok()
                        .expect("Error in WindowBuilder"),
            state: GameState::Running,
            input: self.input,
        }
    }
}

But this code complains in the last line input: self.input with this:

error: cannot move out of borrowed content

I think I understand why. Since the argument passed in the function is &self, I cannot take ownership of it, and that what the last line is doing.

I thought that maybe changing &self to self would work, but then the compile argues that I cannot mutate self.

There is also the Copy trait from what I know, and that maybe should solve the problem. But Input is basically a HashMap, which means that a copy could be expensive if the hash itself is too big.

How would be a nice way of solving this problem?

Edit:

I tried doing this:

#[derive(Debug, Copy, Clone)]
struct Input {
    keys_pressed: HashMap<VirtualKeyCode, bool>,
}

But the compiler complains:

error: the trait `Copy` may not be implemented for this type; field `keys_pressed` does not implement `Copy`
1
The Copy trait can only be used for types that are trivially copyable, this requires not having any internal heap allocated memory.Matthieu M.
It seems that I cannot do that for Input because HashMap does not implement Copylhahn
@ker: To be honest, had the OP included the code with the chain call, it would have been more obvious!Matthieu M.
MCVEs make the world so much easier...oli_obk
@lhahn: MCVE; but don't worry, we all know it's not always obvious to manage to reduce the code sufficiently and STILL exhibit the problem.Matthieu M.

1 Answers

7
votes

Given how your method signatures are formulated, you appear to be aiming for chaining:

let game = GameBuilder::new().with_dimensions(...)
                             .with_title(...)
                             .build();

In Rust, this requires that GameBuilder be passed by value:

pub fn with_dimensions(self, ...) -> GameBuilder {
    // ...
}

And in order to be able to mutate self within the method, you need to make it mut:

pub fn with_dimensions(mut self, ...) -> GameBuilder {
}

If you change the signature of with_dimensions, with_title, game_keys and build to take self by value (mut self if mutation is intended), then chaining should work.