Невозможно выйти из заимствованного содержимого и шаблона Builder

Я только изучаю Rust. Я пытаюсь создать строительную структуру для моей игровой структуры. Вот код:

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,
        }
    }
}

Но этот код жалуется в последней строке input: self.input на следующее:

error: cannot move out of borrowed content

Думаю, я понимаю почему. Поскольку аргумент, переданный в функции, равен &self, я не могу взять на себя ответственность за него и за то, что делает последняя строка.

Я подумал, что, возможно, изменение &self на self сработает, но затем компиляция утверждает, что я не могу изменить self.

Из того, что я знаю, есть также черта Копировать, и это, возможно, должно решить проблему. Но Input - это в основном HashMap, а это означает, что копия может быть дорогостоящей, если сам хеш слишком велик.

Как было бы неплохо решить эту проблему?

Редактировать:

Я пробовал это сделать:

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

Но компилятор жалуется:

error: the trait `Copy` may not be implemented for this type; field `keys_pressed` does not implement `Copy`

person lhahn    schedule 06.08.2015    source источник
comment
Признак Copy может использоваться только для типов, которые легко копируются, для этого не требуется никакой внутренней памяти, выделенной в куче.   -  person Matthieu M.    schedule 06.08.2015
comment
Кажется, я не могу сделать это для ввода, потому что HashMap не реализует Copy   -  person lhahn    schedule 06.08.2015
comment
@ker: Сначала я подумал о том же, а затем понял, что, вероятно, это вызов with_title().build(), и, таким образом, build получает &mut self и не может владеть им ... таким образом, фактически требуется изменение полного набора методов, чтобы каждый раз передавать право собственности на включить цепочку.   -  person Matthieu M.    schedule 06.08.2015
comment
да, я удалил свой закрытый голос ... Я был немного переусердствовал   -  person oli_obk    schedule 06.08.2015
comment
@ker: Если честно, если бы OP включил код с вызовом цепочки, это было бы более очевидно!   -  person Matthieu M.    schedule 06.08.2015
comment
MCVE делают мир намного проще ...   -  person oli_obk    schedule 06.08.2015
comment
@lhahn: MCVE; но не волнуйтесь, мы все знаем, что не всегда очевидно, что удается достаточно сократить код, и ВСЕ ЕЩЕ проявляют проблему.   -  person Matthieu M.    schedule 06.08.2015
comment
Да, я только что это прочитал. Постараюсь сделать это в следующий раз.   -  person lhahn    schedule 06.08.2015
comment
@lhahn, еще не поздно - держу пари, вы всегда можете обновить свой код, чтобы он еще лучше продемонстрировал проблему!   -  person Shepmaster    schedule 06.08.2015


Ответы (1)


Учитывая, как формулируются сигнатуры ваших методов, вы, похоже, стремитесь к цепочке:

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

В Rust для этого требуется, чтобы GameBuilder передавался по значению:

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

И чтобы иметь возможность изменять self внутри метода, вам нужно сделать это mut:

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

Если вы измените сигнатуру with_dimensions, with_title, game_keys и build так, чтобы self принималось по значению (mut self, если предполагается изменение), то цепочка должна работать.

person Matthieu M.    schedule 06.08.2015
comment
ах, я не видел цепочки ... это объясняет, откуда взялась ошибка - person oli_obk; 06.08.2015