无法搬出借用的内容和Builder模式
Cannot move out of borrowed content and Builder pattern
我正在学习 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
.
据我所知,还有 Copy 特性,也许应该可以解决问题。但 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`
鉴于您的方法签名的制定方式,您的目标似乎是链接:
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
如果突变是有意的),那么链接应该可以工作。
使用 Option
和 take()
尝试构建器模式
示例:
#[derive(PartialEq, Debug)]
struct Game {
window: Window,
}
#[derive(PartialEq, Debug)]
struct Window {
title: String,
dimensions: (u32, u32),
}
struct GameBuilder {
window_title: Option<String>,
window_dimensions: Option<(u32, u32)>,
}
impl GameBuilder {
fn new() -> Self {
Self {
window_title: None,
window_dimensions: None,
}
}
fn window_title(&mut self, window_title: &str) -> &mut Self {
self.window_title = Some(window_title.to_owned());
self
}
fn window_dimensions(&mut self, width: u32, height: u32) -> &mut Self {
self.window_dimensions = Some((width, height));
self
}
fn build(&mut self) -> Result<Game, Box<dyn std::error::Error>> {
Ok(Game {
window: Window {
// `ok_or(&str)?` works, because From<&str> is implemented for Box<dyn Error>
title: self.window_title.take().ok_or("window_title is unset")?,
dimensions: self
.window_dimensions
.take()
.ok_or("window_dimensions are unset")?,
},
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let mut builder = GameBuilder::new();
builder.window_title("Awesome Builder");
builder.window_dimensions(800, 600);
let game = builder.build();
assert_eq!(
game.expect("build success"),
Game {
window: Window {
title: "Awesome Builder".into(),
dimensions: (800, 600)
}
}
);
}
#[test]
fn test_1() {
let game2 = GameBuilder::new()
.window_title("Busy Builder")
.window_dimensions(1234, 123)
.build();
assert_eq!(
game2.expect("build success"),
Game {
window: Window {
title: "Busy Builder".into(),
dimensions: (1234, 123),
}
}
)
}
}
我正在学习 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
.
据我所知,还有 Copy 特性,也许应该可以解决问题。但 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`
鉴于您的方法签名的制定方式,您的目标似乎是链接:
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
如果突变是有意的),那么链接应该可以工作。
使用 Option
和 take()
示例:
#[derive(PartialEq, Debug)]
struct Game {
window: Window,
}
#[derive(PartialEq, Debug)]
struct Window {
title: String,
dimensions: (u32, u32),
}
struct GameBuilder {
window_title: Option<String>,
window_dimensions: Option<(u32, u32)>,
}
impl GameBuilder {
fn new() -> Self {
Self {
window_title: None,
window_dimensions: None,
}
}
fn window_title(&mut self, window_title: &str) -> &mut Self {
self.window_title = Some(window_title.to_owned());
self
}
fn window_dimensions(&mut self, width: u32, height: u32) -> &mut Self {
self.window_dimensions = Some((width, height));
self
}
fn build(&mut self) -> Result<Game, Box<dyn std::error::Error>> {
Ok(Game {
window: Window {
// `ok_or(&str)?` works, because From<&str> is implemented for Box<dyn Error>
title: self.window_title.take().ok_or("window_title is unset")?,
dimensions: self
.window_dimensions
.take()
.ok_or("window_dimensions are unset")?,
},
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let mut builder = GameBuilder::new();
builder.window_title("Awesome Builder");
builder.window_dimensions(800, 600);
let game = builder.build();
assert_eq!(
game.expect("build success"),
Game {
window: Window {
title: "Awesome Builder".into(),
dimensions: (800, 600)
}
}
);
}
#[test]
fn test_1() {
let game2 = GameBuilder::new()
.window_title("Busy Builder")
.window_dimensions(1234, 123)
.build();
assert_eq!(
game2.expect("build success"),
Game {
window: Window {
title: "Busy Builder".into(),
dimensions: (1234, 123),
}
}
)
}
}