Copy and Clone triat in Rust

2024-05-15 21:02:07 浏览数 (2)

在 rust 中,有 CopyClone 两个 trait 用于“复制”,本文区别两者。

Clone trait

std::clone::Clone triat 的定义如下:

代码语言:javascript复制
pub trait Clone: Sized {
    // Required method
    fn clone(&self) -> Self;

    // Provided method
    fn clone_from(&mut self, source: &Self) { ... }
}

Clone triat 用于给对象加上“复制”的能力,需要 x.clone() 来显式调用。对象的“复制”既可以是浅拷贝,也可以是深拷贝。

Copy trait

std::marker::Copy triat 是一个 marker trait,定义如下:

代码语言:javascript复制
pub trait Copy: Clone { }

Copy trait 用于标记对象的“复制”只要浅拷贝即可,是可以简单、直接地逐位复制实现数据的完整拷贝。此时,拷贝将由编译器隐式实现。

拷贝隐式实现的含义

在 rust 中,变量赋值默认是移动语义(move semantics),比如:

代码语言:javascript复制
#![allow(unused)]
fn main() {
  let s = String::from("Hello, world!"); // String implements Clone trait but not Copy trait
  let scopy = s; // s is move to scopy, and so cannot be used
  println!("{s:?}"); // ❌
}

当对象实现 Copytriat,变量赋值将是复制语义(copy semantics):

代码语言:javascript复制
#![allow(unused)]
fn main() {
  let s: i32 = 12; // i32 implements Copy trait
  let scopy = s; // s is copy to scopy, which still can be used
  println!("{s:?}"); // ✔️
}

所以不是所有的类型都能实现 Copytrait 的 [1],比如:

  • String:栈上的指针,指向堆中的 buffer。Copy 只会拷贝栈上的指针,导致二次释放(double free)。
  • Vec<T>:同样是指针,指向堆中的数组。
  • &mut T:会导致创建多个可变引用

总结起来:

  • 不是完全在栈上
  • 浅拷贝会破坏 rust 的所有权规则

的类型不能实现 Copy trait。

CopyDroptrait 的关系

类型实现了 Droptriat,表明清理该类型对象比较复杂,而不是简单从栈上删除,该变量也就不应该实现 Copytriat。

rustc 事实上会阻止实现了 Drop trait 的类型实现 Copy trait。[2]

总结

Clone trait 是 Copytriat 的 super-trait。想实现 Copytrait 一定要实现 Clonetrait,因为拷贝能力实际是由 Clonetrait 实现的,Copytrait 只是一个 marker triat,标记该类型的拷贝是低成本的。

  1. https://oswalt.dev/2023/12/copy-and-clone-in-rust/ ↩︎
  2. https://doc.rust-lang.org/std/marker/trait.Copy.html#when-cant-my-type-be-copy ↩︎

0 人点赞