#[derive(Debug)]structFoobar(i32);fnuses_foobar(foobar:Foobar) {println!("I consumed a Foobar: {:?}", foobar);}fnmain() {let x =Foobar(1);uses_foobar(x);}
在 main 函数中,x 包含 Foobar。 当调用 uses_foobar 时,会将该 Foobar 的所有权将传递给 uses_foobar。 在 main 中再次使用该 x 将会是一个错误:
#[derive(Debug)]structFoobar(i32);fnuses_foobar(foobar:Foobar) {println!("I consumed a Foobar: {:?}", foobar);}fnmain() {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 here11|uses_foobar(x);|^ value used here after move|= note:move occurs because `x` has type `Foobar`, which does not implement the `Copy` traiterror: aborting due to previous errorFor more information about this error, try `rustc --explain E0382`.
销毁
当一个值超出作用域时,使用它的 Drop trait (类似 typeclass) ,然后释放内存。 我们可以通过为 Foobar 编写一个 Drop 实现来说明这一点:
在看到输出之前,猜猜下面这个程序的输出是什么。
#[derive(Debug)]structFoobar(i32);implDropforFoobar {fndrop(&mut self) {println!("Dropping a Foobar: {:?}", self); }}fnuses_foobar(foobar:Foobar) {println!("I consumed a Foobar: {:?}", foobar);}fnmain() {let x =Foobar(1);println!("Before uses_foobar");uses_foobar(x);println!("After uses_foobar");}
输出:
Before uses_foobarI 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 hereerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0505`.
引用其他的可变引用
您还可以对值进行可变引用。 为了避免数据竞赛,Rust 不允许以任何其他方式同时引用和访问值。
fnmain() {letmut x:Foobar=Foobar(1);let y:&mutFoobar=&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)]structFoobar(i32);fnmain() {let x =Foobar(1);foo(x);foo(x);letmut y =Foobar(2);bar(&y);bar(&y);let z =&mut y;bar(&y);baz(&mut y);baz(z);}// movefnfoo(_foobar:Foobar) {}// read only referencefnbar(_foobar:&Foobar) {}// mutable referencefnbaz(_foobar:&mutFoobar) {}
#[derive(Debug)]structFoobar(i32);fnmain() {let x =Foobar(1); x.0=2; // changes the 0th value inside the productprintln!("{:?}", x);}
显然你不能改变 x,但是让我们稍微改变一下:
#[derive(Debug)]structFoobar(i32);fnmain() {let x =Foobar(1);foo(x);}fnfoo(mut x:Foobar) { x.0=2; // changes the 0th value inside the productprintln!("{:?}", x);}
在学习 Rust 之前,我会反对这一点: x 是不可变的,因此我们不应该允许将它传递给一个需要可变 x 的函数。 这里的可变性是变量的一个特性,而不是值本身。 当你把 x 移动到 foo 中,main 不再有权限访问 x,也不在乎它是否变异了。 在 foo 中,我们已经明确指出 x 可以变异,所以我们很酷。
这与 Haskell 的观点大相径庭。
Copy trait
上一次我们讨论这个主题时,使用了数值类型vs字符串。让我们再努力一点,下面的代码是否编译?
fnuses_i32(i:i32) {println!("I consumed an i32: {}", i);}fnmain() {let x =1;uses_i32(x);uses_i32(x);}
#[derive(Debug, Clone)]structFoobar(i32);implDropforFoobar {fndrop(&mut self) {println!("Dropping: {:?}", self); }}fnuses_foobar(foobar:Foobar) {println!("I consumed a Foobar: {:?}", foobar);}fnmain() {let x =Foobar(1);uses_foobar(x.clone());uses_foobar(x);}
为什么我们不需要在第二个 uses_foobar 时使用 x.clone()?
练习3
更改下面代码,而无需完全修改 main 函数,就可以让它成功地编译和运行。 一些提示:Debug 是可以自动派生的特殊trait,为了获得 Copy 实现,您还需要一个 Clone 实现。
#[derive(Debug)]structFoobar(i32);fnuses_foobar(foobar:Foobar) {println!("I consumed a Foobar: {:?}", foobar);}fnmain() {let x =Foobar(1);uses_foobar(x);uses_foobar(x);}
首先,我们将再次得到一个关于 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 herewrite!(fmt, "|\n");
继续使用 cargo run 运行程序,你会有一个不愉快的惊喜:
error[E0501]: cannot borrow `*fmt` as mutable because previous closure requires unique access--> src/main.rs:91:20|80|letmut top_bottom =|| {|-- closure construction occurs here81|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
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