Rust 错误码 E0507

Rust 错误码 E0507
1	let args: Vec<_> = env::args().collect();
2	let query = args[1]; // error

move = take ownership = 取得所有权。

borrow = don't take ownership = 借用。

move 常见案例

 1// _Stack_ 分配的变量
 2let x = 5u32;
 3// *Copy* `x` 到 `y` - 所有权未发生转移,未发生 move
 4let y = x;
 5// 两个变量可以同时使用
 6println!("x is {}, and y is {}", x, y);
 7
 8// `a` 是指向 _heap_ 上分配的变量的 pointer
 9let a = Box::new(5i32);
10
11println!("a contains: {}", a);
12
13// *Move* `a` 到 `b`
14let b = a;
15// `a` 的 pointer 地址(不是数据)复制给了 `b`.
16// 两者都是指向同一片 heap 区域的 pointer,但是
17// `b` 现在取得了这块数据的所有权.
18
19// Error! `a` can no longer access the data, because it no longer owns the
20// heap memory
21//println!("a contains: {}", a);
22// TODO ^ 此处将会报错
1// 此函数演示如何释放一个变量拥有的内存区域
2fn destroy_box(c: Box<i32>) {
3    println!("Destroying a box that contains {}", c);
4
5    // `c` is destroyed and the memory freed
6}

borrow 常见案例

1// 这个函数发生了所有权转移,变量会被销毁
2fn eat_box_i32(boxed_i32: Box<i32>) {
3    println!("Destroying box that contains {}", boxed_i32);
4}
5
6// 这个函数只是借用了变量,不会销毁它
7fn borrow_i32(borrowed_i32: &i32) {
8    println!("This int is: {}", borrowed_i32);
9}

A borrowed value was moved out.

一个借用的变量被 move 了(你不能 move 一个 borrow 来的变量)。

错误代码示例:

 1use std::cell::RefCell;
 2
 3struct TheDarkKnight;
 4
 5impl TheDarkKnight {
 6    fn nothing_is_true(self) {}
 7}
 8
 9fn main() {
10    let x = RefCell::new(TheDarkKnight);
11
12    x.borrow().nothing_is_true(); // error: cannot move out of borrowed content
13}

这里, nothing_is_true 方法取得了 self 所有权. 但是, self 不能被 move,因为 .borrow() 返回的是 &TheDarkKnight, 它是一个借用的变量,所有权在 RefCell.

有以下几种方法,可以修复这个问题:

 1use std::cell::RefCell;
 2
 3struct TheDarkKnight;
 4
 5impl TheDarkKnight {
 6    fn nothing_is_true(&self) {} // 不要取得所有权
 7}
 8
 9fn main() {
10    let x = RefCell::new(TheDarkKnight);
11
12    x.borrow().nothing_is_true(); // ok!
13}

或:

 1use std::cell::RefCell;
 2
 3struct TheDarkKnight;
 4
 5impl TheDarkKnight {
 6    fn nothing_is_true(self) {}
 7}
 8
 9fn main() {
10    let x = RefCell::new(TheDarkKnight);
11    let x = x.into_inner(); // 拿回了所有权
12
13    x.nothing_is_true(); // ok!
14}

或:

 1use std::cell::RefCell;
 2
 3#[derive(Clone, Copy)] // 实现 Copy 特质
 4struct TheDarkKnight;
 5
 6impl TheDarkKnight {
 7    fn nothing_is_true(self) {}
 8}
 9
10fn main() {
11    let x = RefCell::new(TheDarkKnight);
12
13    x.borrow().nothing_is_true(); // ok!
14}

move 一个 mutably borrowed struct 的成员,依然会报错 E0507:

 1struct TheDarkKnight;
 2
 3impl TheDarkKnight {
 4    fn nothing_is_true(self) {}
 5}
 6
 7struct Batcave {
 8    knight: TheDarkKnight
 9}
10
11fn main() {
12    let mut cave = Batcave {
13        knight: TheDarkKnight
14    };
15    let borrowed = &mut cave;
16
17    borrowed.knight.nothing_is_true(); // E0507
18}

但是如果使用 mem::replace 把一些东西放回去就没事:

 1# struct TheDarkKnight;
 2# impl TheDarkKnight { fn nothing_is_true(self) {} }
 3# struct Batcave { knight: TheDarkKnight }
 4use std::mem;
 5
 6let mut cave = Batcave {
 7    knight: TheDarkKnight
 8};
 9let borrowed = &mut cave;
10
11mem::replace(&mut borrowed.knight, TheDarkKnight).nothing_is_true(); // ok!

一个从 move 修改为 borrowed 的案例

  1. move(编译错误)
    1fn main() {
    2    let s1 = String::from("hello");
    3    let len = calculate_length(s1);
    4    println!("The length of '{s1}' is {len}."); // error[E0382] 使用一个已经被 move 的变量
    5}
    6
    7fn calculate_length(s: String) -> usize {
    8    s.len()
    9}
    
  2. borrow
     1fn main() {
     2    let s1 = String::from("hello");
     3
     4    let len = calculate_length(&s1);
     5
     6    println!("The length of '{s1}' is {len}.");
     7}
     8
     9fn calculate_length(s: &String) -> usize {
    10    s.len()
    11}
    
  3. borrow 后尝试修改(编译错误)
    1fn main() {
    2 let s = String::from("hello");
    3 change(&s);
    4}
    5
    6fn change(some_string: &String) {
    7   some_string.push_str(", world"); // error[E0596] 尝试 mut borrow 一个不能修改的变量
    8}
    
  4. mutably borrow
    1fn main() {
    2    let mut s = String::from("hello");
    3
    4    change(&mut s);
    5}
    6
    7fn change(some_string: &mut String) {
    8    some_string.push_str(", world");
    9}
    

要了解更多 Rust’s 的所有权设计,请移步 References & Borrowing