用带有字符串字段的struct解释Rust所有权概念

22-10-07 banq

带有字符串字段的结构struct:
如果一个类型的所有组件都实现了Copy,那么它就可以实现Copy(copy类似语言自身的原型模式概念);

在下面这些列表中,我们关注的是Movie结构,它由一个没有实现Copy的String字段组成(原因见这里),因此,Movie不能实现Copy。

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}

到目前为止看起来不错,不是吗?程序编译完毕(忽略了未使用字段的警告),我们都很高兴。

输出:

Movie: Movie { title: "Rust" }!


但在加入println!后:

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(movie);
    println!("Movie: {:?}", movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}

编译器抱怨说我们试图借用一个已经移到do_something的值Movie。
编译器报错:

error[E0382]: borrow of moved value: `movie`
  --> src/main.rs:10:29


我们可以借用Movie而不是移动它。(Movie中有String字段,String不是值复制,也就是值无法移动的)

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(&movie);
    println!("Movie: {:?}", movie);
}
fn do_something(movie: &Movie) {
    println!("Movie: {:?}!", movie);
}

输出:

Movie: Movie { title: "Rust" }!
Movie: Movie { title: "Rust" }



或者我们可以为do_something克隆movie。这需要Movie实现Clone。

#[derive(Debug, Clone)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    do_something(movie.clone());
    println!("Movie: {:?}", movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}


或者,如果你的程序允许的话,我们可以在不克隆的情况下把两个代码行调换一下顺序。在这里,把movie借给println!,然后把movie移到do_something。

#[derive(Debug)]
struct Movie {
    title: String,
}
fn main() {
    let movie = Movie { 
        title: String::from("Rust")
    };
    println!("Movie: {:?}", movie);
    do_something(movie);
}
fn do_something(movie: Movie) {
    println!("Movie: {:?}!", movie);
}


Struct 包含u8 字段
如前所述,如果一个类型的所有组件都实现了Copy,它就可以实现Copy。
在下面列表中,我们关注的是由一个u8字段组成的Book结构,它实现了Copy特性。因此,Movie应该可以实现Copy的。

#[derive(Debug)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}


直到我们在do_something下面添加一个println!语句。发生了什么?

因为book没有实现Copy特性(或者还没有实现),Rust将book移动到do_something中。但是......因为book被移动了,println! 不能再使用这个值。

#[derive(Debug)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
    println!("Book: {:?}", book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}


报错:

error[E0382]: borrow of moved value: `book`
 --> src/main.rs:8:28


一种方法是通过派生Copy为Book实现Copy特性:

#[derive(Debug, Copy)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
    println!("Book: {:?}", book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}


还是报错:

error[E0277]: the trait bound `Book: Clone` is not satisfied
 --> src/main.rs:1:17


但是......我们仍然得到一个错误--triay绑定的Book。克隆没有得到满足。

如果我们去看文档,它说所有属于Copy的东西都必须同时实现Clone,因为Clone是一个超级属性。

因此,让我们在现有的Copy特质之上为Book实现Clone特质。

#[derive(Debug, Copy, Clone)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(book);
    println!("Book: {:?}", book);
}
fn do_something(book: Book) {
    println!("Book: {:?}!", book);
}


正常输出:

Book: Book { id: 1 }!
Book: Book { id: 1 }


我们也可以重新设计我们的程序,让 do_something 借用Book:

#[derive(Debug)]
struct Book {
    id: u8,
}
fn main() {
    let book = Book { id: 1 };
    do_something(&book);
    println!("Book: {:?}", book);
}
fn do_something(book: &Book) {
    println!("Book: {:?}!", book);
}