复制值,第一部分 (Copying values, pt. 1)

上一章我们引入了所有权 (ownership) 与借用 (borrowing)。
我们特别强调过:

  • Rust 中每个值在任意时刻只有一个所有者 (owner)。
  • 当函数获取一个值的所有权("消费 (consume) 它")时,调用方再也不能使用那个值。

这些限制有时颇为受限。
有时我们必须调用一个会拿走所有权的函数,但之后还想继续使用那个值。

fn consumer(s: String) { /* */ }

fn example() {
     let mut s = String::from("hello");
     consumer(s);
     s.push_str(", world!"); // error: value borrowed here after move
}

这就是 Clone 登场的时候。

Clone

Clone 是 Rust 标准库中定义的一个特质:

pub trait Clone {
    fn clone(&self) -> Self;
}

它的方法 clone 接受 self 的引用,返回一个新的、由调用方拥有 (owned) 的同类型实例。

实战 (In action)

回到上面的例子,我们可以在调用 consumer 之前用 clone 创建一个新的 String 实例:

fn consumer(s: String) { /* */ }

fn example() {
     let mut s = String::from("hello");
     let t = s.clone();
     consumer(t);
     s.push_str(", world!"); // 不再报错
}

我们不把 s 的所有权交给 consumer,而是创建一个新的 String(通过克隆 s),把它交给 consumer
sconsumer 调用之后仍然有效、仍然可用。

在内存中 (In memory)

我们看看上面例子在内存中发生了什么。 当 let mut s = String::from("hello"); 执行时,内存看起来是这样:

                    s
      +---------+--------+----------+
Stack | pointer | length | capacity | 
      |  |      |   5    |    5     |
      +--|------+--------+----------+
         |
         |
         v
       +---+---+---+---+---+
Heap:  | H | e | l | l | o |
       +---+---+---+---+---+

let t = s.clone() 执行时,会在堆上分配一整块新区域来存储数据的副本:

                    s                                    t
      +---------+--------+----------+      +---------+--------+----------+
Stack | pointer | length | capacity |      | pointer | length | capacity |
      |  |      |   5    |    5     |      |  |      |   5    |    5     |
      +--|------+--------+----------+      +--|------+--------+----------+
         |                                    |
         |                                    |
         v                                    v
       +---+---+---+---+---+                +---+---+---+---+---+
Heap:  | H | e | l | l | o |                | H | e | l | l | o |
       +---+---+---+---+---+                +---+---+---+---+---+

如果你来自像 Java 这样的语言,可以把 clone 想成创建对象深拷贝 (deep copy) 的方式。

实现 Clone (Implementing Clone)

要让一个类型可被 Clone,我们需要为它实现 Clone 特质。
通常情况下,你都会通过派生 (derive) 来实现 Clone

#[derive(Clone)]
struct MyType {
    // 字段
}

编译器会按你预期的方式为 MyType 实现 Clone:分别克隆 MyType 的每个字段,再用克隆出的字段构造一个新的 MyType 实例。
记得你可以用 cargo expand(或你的 IDE)来查看派生 (derive) 宏生成的代码。

原文链接:英文原文