Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Clone 与 Copy

在处理资源时,默认行为是在赋值或函数调用期间转移它们。然而,有时我们也需要复制资源。

Clone trait 帮助我们实现这一点。最常见的是,我们可以使用 Clone trait 定义的 .clone() 方法。

Copy:隐式克隆

Copy trait 允许一个类型仅通过复制其比特位来完成复制,无需任何额外逻辑。当一个类型实现了 Copy 时,赋值和函数调用会隐式地复制该值,而不是移动它。

重要: Copy 依赖 Clone —— 任何实现了 Copy 的类型都必须同时实现 Clone。这是因为 Copy 被定义为一个子 trait:trait Copy: Clone {}Copy 类型的 Clone 实现只是简单地复制比特位。

并非所有类型都能实现 Copy。一个类型只有满足以下条件时才能是 Copy 的:

  • 它的所有组成部分都是 Copy
  • 它不管理外部资源(如堆内存、文件句柄等)
// 一个不包含资源的单元结构体
// 注意:Copy 依赖 Clone,所以我们必须同时派生两者
#[derive(Debug, Clone, Copy)]
struct Unit;

// 一个包含资源、并实现了 `Clone` trait 的元组结构体
// 它不能是 Copy 的,因为 Box<T> 不是 Copy
#[derive(Clone, Debug)]
struct Pair(Box<i32>, Box<i32>);

fn main() {
    // 实例化 `Unit`
    let unit = Unit;
    // 复制 `Unit` —— 这是一次隐式复制,而不是移动!
    // 因为 Unit 实现了 Copy,该值会被自动复制
    let copied_unit = unit;

    // 两个 `Unit` 可以独立使用
    println!("原始: {:?}", unit);
    println!("复制: {:?}", copied_unit);

    // 实例化 `Pair`
    let pair = Pair(Box::new(1), Box::new(2));
    println!("原始: {:?}", pair);

    // 把 `pair` 移动到 `moved_pair`,资源也随之移动
    // Pair 没有实现 Copy,所以这是一次移动
    let moved_pair = pair;
    println!("已移动: {:?}", moved_pair);

    // 错误!`pair` 已失去其资源
    //println!("原始: {:?}", pair);
    // TODO ^ 尝试取消注释此行

    // 把 `moved_pair` 克隆到 `cloned_pair`(包含其资源)
    // 与 Copy 不同,Clone 是显式的——我们必须调用 .clone()
    let cloned_pair = moved_pair.clone();
    // 使用 std::mem::drop 丢弃已移动的原始对
    drop(moved_pair);

    // 错误!`moved_pair` 已被丢弃
    //println!("已移动并丢弃: {:?}", moved_pair);
    // TODO ^ 尝试取消注释此行

    // .clone() 的结果仍然可以使用!
    println!("克隆: {:?}", cloned_pair);
}