如何为包含String(或任何未实现Copy的类型)的类型实现复制和克隆?

99

我在Rust中有一个枚举类型,其中有一个值接受一个String

#[derive(Clone, Copy)]
enum Simple {
    Error(String),
    Okay,
    Foo([u32; 5]),
}

fn main() {
    let x = Simple::Error(String::from("blah"));
    let y = x.clone();
}

上面的枚举值Foo代表我使用的大约10个其他枚举类型,它们接受可复制的类型或这些类型的数组。编译器似乎对它们没有抱怨,只有Error(String)引起了这个问题:

error[E0204]: the trait `Copy` may not be implemented for this type
 --> src/main.rs:1:17
  |
1 | #[derive(Clone, Copy)]
  |                 ^^^^
2 | enum Simple {
3 |     Error(String),
  |           ------ this field does not implement `Copy`
  |

由于某些原因,String 无法被复制。我不太理解这一点。当使用默认实现处理其他类型时,如何为一个枚举类型实现 Clone,只针对其中一个有问题的类型呢?

2个回答

101

复制(Copy)

Copy 指定了可以通过位拷贝创建一个有效实例而不会使原始实例无效的类型。

对于 String 来说,这并不是真的,因为 String 包含指向堆上字符串数据的指针,并假设它独占该数据的所有权。当你丢弃一个 String 时,它会释放堆上的数据。如果你对一个 String 进行位拷贝,则两个实例都会尝试释放相同的内存块,这是未定义的行为

由于 String 没有实现 Copy,所以你的 enum 也不能实现 Copy,因为编译器强制要求 Copy 类型仅由 Copy 数据成员组成。

克隆(Clone)

Clone 只提供了一个标准的 clone 方法,每个实现者决定如何实现它。 String 确实实现了 Clone,所以你可以在你的 enum 上放置 #[derive(Clone)]


5
我应该补充一下,我不明白为什么在原则或实践中字符串不能实现复制特性 - QT中的QString对象可以被复制并且它们共享和维护一个内部缓冲区。如果复制对象执行可变操作,则首先克隆缓冲区,以便其他副本仍然持有对原始缓冲区的引用。在QT中,通过引用传递仍然是更好的做法,以避免原子引用计数,但是复制也同样有效率。 - locka
8
@locka 因为*Copy指定了能够通过位拷贝创建有效实例的类型*。复制这种字符串的比特位不会增加原子计数器,因为这不再是复制。你可以自行选择需要的保证。你可以共享所有权跨线程共享所有权,实现写时复制等。对于系统语言来说,为您做出这个决定(并加重代码负担)是不好的。 - Shepmaster
什么是“按位复制”? - cakraww
5
位拷贝是使用 memcpy 或类似函数进行的所有字节的浅层复制。它与成员逐一复制、位逐一复制和浅层复制不同。 - MG lolenstine
@locka 一种需要分配的类型不能是Copy,因为只能执行一次释放操作,否则会出现双重释放。如果你可以复制字符串,那么哪个实例将负责释放缓冲区呢?你所描述的基本上是Cow<str>Rc<str>,它们也因为同样的原因不能是Copy:你需要正确调用drop来管理缓冲区并最终释放它。 - undefined

9

我进行了一些探索,看看手动实现枚举会是什么样子。我想出了这个方法,但请记住,您也可以像其他地方所述的那样使用 #[derive(Clone)],编译器将为您执行此操作。

enum Simple {
    Error(String),
    Okay,
    Foo([u32; 5]),
}

impl Clone for Simple {
    fn clone(&self) -> Simple {
        match self {
            Error(a) => Error(a.to_string()),
            Okay => Okay,
            Foo(a) => Foo(a.clone()),
        }
    }
}

6
对于 #[derive(Clone, Copy)] pub enum ServerResponse { Ok(String), Err(String), },我遇到了 error[E0204]: the trait 'Copy' may not be implemented for this type 的错误提示。其中 Ok(String) 这个字段没有实现 'Copy'。手动实现 'Copy' 也会导致类似的错误。 - Günter Zöchbauer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接