在 main 函数中,x 包含 Foobar。 当调用 uses_foobar 时,会将该 Foobar 的所有权将传递给 uses_foobar。 在 main 中再次使用该 x 将会是一个错误:
#[derive(Debug)]
struct Foobar(i32);
fn uses_foobar(foobar: Foobar) {
println!("I consumed a Foobar: {:?}", foobar);
}
fn main() {
let x = Foobar(1);
uses_foobar(x);
uses_foobar(x);
}
结果如下:
error[E0382]: use of moved value: `x`
--> foo.rs:11:17
|
10 | uses_foobar(x);
| - value moved here
11 | uses_foobar(x);
| ^ value used here after move
|
= note: move occurs because `x` has type `Foobar`, which does not implement the `Copy` trait
error: aborting due to previous error
For more information about this error, try `rustc --explain E0382`.
销毁
当一个值超出作用域时,使用它的 Drop trait (类似 typeclass) ,然后释放内存。 我们可以通过为 Foobar 编写一个 Drop 实现来说明这一点:
在看到输出之前,猜猜下面这个程序的输出是什么。
#[derive(Debug)]
struct Foobar(i32);
impl Drop for Foobar {
fn drop(&mut self) {
println!("Dropping a Foobar: {:?}", self);
}
}
fn uses_foobar(foobar: Foobar) {
println!("I consumed a Foobar: {:?}", foobar);
}
fn main() {
let x = Foobar(1);
println!("Before uses_foobar");
uses_foobar(x);
println!("After uses_foobar");
}
输出:
Before uses_foobar
I consumed a Foobar: Foobar(1)
Dropping a Foobar: Foobar(1)
After uses_foobar
error[E0505]: cannot move out of `x` because it is borrowed
--> foo.rs:19:20
|
16 | let y: &Foobar = &x;
| - borrow of `x` occurs here
...
19 | std::mem::drop(x);
| ^ move out of `x` occurs here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0505`.
引用其他的可变引用
您还可以对值进行可变引用。 为了避免数据竞赛,Rust 不允许以任何其他方式同时引用和访问值。
fn main() {
let mut x: Foobar = Foobar(1);
let y: &mut Foobar = &mut x;
println!("Before uses_foobar");
uses_foobar(&x); // will fail!
uses_foobar(y);
println!("After uses_foobar");
}
注意 y 的类型现在是 &mut Foobar。 像Haskell一样,Rust在类型级别跟踪可变性。 好极了!
挑战
尝试猜测下面代码中的哪些行会触发编译错误:
#[derive(Debug)]
struct Foobar(i32);
fn main() {
let x = Foobar(1);
foo(x);
foo(x);
let mut y = Foobar(2);
bar(&y);
bar(&y);
let z = &mut y;
bar(&y);
baz(&mut y);
baz(z);
}
// move
fn foo(_foobar: Foobar) {
}
// read only reference
fn bar(_foobar: &Foobar) {
}
// mutable reference
fn baz(_foobar: &mut Foobar) {
}
#[derive(Debug)]
struct Foobar(i32);
fn main() {
let x = Foobar(1);
x.0 = 2; // changes the 0th value inside the product
println!("{:?}", x);
}
显然你不能改变 x,但是让我们稍微改变一下:
#[derive(Debug)]
struct Foobar(i32);
fn main() {
let x = Foobar(1);
foo(x);
}
fn foo(mut x: Foobar) {
x.0 = 2; // changes the 0th value inside the product
println!("{:?}", x);
}
在学习 Rust 之前,我会反对这一点: x 是不可变的,因此我们不应该允许将它传递给一个需要可变 x 的函数。 这里的可变性是变量的一个特性,而不是值本身。 当你把 x 移动到 foo 中,main 不再有权限访问 x,也不在乎它是否变异了。 在 foo 中,我们已经明确指出 x 可以变异,所以我们很酷。
这与 Haskell 的观点大相径庭。
Copy trait
上一次我们讨论这个主题时,使用了数值类型vs字符串。让我们再努力一点,下面的代码是否编译?
fn uses_i32(i: i32) {
println!("I consumed an i32: {}", i);
}
fn main() {
let x = 1;
uses_i32(x);
uses_i32(x);
}
let top_bottom = || {
write!(fmt, "+");
for _ in 0..self.frame.width {
write!(fmt, "-");
}
write!(fmt, "+\n");
};
top_bottom();
top_bottom();
首先,我们将再次得到一个关于 Result 和()的错误。 你需要删除两个分号来解决这个问题。 一旦您完成了这些操作,您将得到一个全新的错误消息。 耶!
error[E0596]: cannot borrow `top_bottom` as mutable, as it is not declared as mutable
--> src/main.rs:88:9
|
80 | let top_bottom = || {
| ---------- help: consider changing this to be mutable: `mut top_bottom`
...
88 | top_bottom();
| ^^^^^^^^^^ cannot borrow as mutable
write!(fmt, "|");
// more code will go here
write!(fmt, "|\n");
继续使用 cargo run 运行程序,你会有一个不愉快的惊喜:
error[E0501]: cannot borrow `*fmt` as mutable because previous closure requires unique access
--> src/main.rs:91:20
|
80 | let mut top_bottom = || {
| -- closure construction occurs here
81 | write!(fmt, "+");
| --- first borrow occurs due to use of `fmt` in closure
...
91 | write!(fmt, "|");
| ^^^ borrow occurs here
...
96 | top_bottom()
| ---------- first borrow used here, in later iteration of loop
还记得 // more code will go here 注释吗? 是时候换掉它了! 我们将为每个列使用另一个 for 循环:
for column in 0..self.frame.width {
write!(fmt, " ");
}
运行 cargo run 会显示一个完整的框架,不错! 不幸的是,它不包括我们的球。 当列与球的 x 相同时,我们需要写一个 o 字符,而不是空格,对于 y 也是一样的:
let c = if row == self.ball.y {
'o'
} else {
' '
};
write!(fmt, "{}", c);
输出有些问题(cargo run完成测试)。修复它,你的渲染功能将完成!
无限循环
我们几乎完成了! 我们还需要在主函数中添加一个无限循环:
打印游戏
游戏步骤
休眠一会儿
fn main () {
let game = Game::new();
let sleep_duration = std::time::Duration::from_millis(33);
loop {
println!("{}", game);
game.step();
std::thread::sleep(sleep_duration);
}
}
这段代码中有一个编译错误。 试着预测它是什么。 如果你不能解决它,问编译器,然后修复它。 运行 cargo run 应该会成功显示你一个弹跳球。
warning: unused `std::result::Result` which must be used
--> src/main.rs:88:9
|
88 | top_bottom(fmt);
| ^^^^^^^^^^^^^^^^
|
= note: this `Result` may be an `Err` variant, which should be handled