Не може да излезе от заимствано съдържание и шаблон на 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
Изглежда, че не мога да направя това за Input, защото 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