目录
- 问题1:Rust Copy、Clone 等 derive 的使用说明,请详细举例 ?
- 1. 先看一个含 String 字段的结构体
- 2. 为什么 String 不能 Copy?
- 3. 如果 String 可以 Copy,会发生什么?
- 4. Rust 实际怎么处理?
- 5. 含 String 字段的 struct 也是一样
- 6. 那为什么 Clone 可以?
- 7. Copy 和 Clone 的本质区别
- 8. 为什么 Rust 不让 Copy 做深拷贝?
- 9. Copy 和 Drop 不能共存
- 10. 对比:纯数字字段的 struct 可以 Copy
- 11. 对比:含 String 字段的 struct 不能 Copy
- 12. 如果字段是 &str,能不能 Copy?
- 13. String 和 &str 的关键区别
- 14. 业务对象为什么一般不建议 Copy?
- 15. 更推荐的写法:读取时传引用,不要随便 clone
- 16. 函数传参中的区别
- 17. 实战设计建议
- 18. 总结
- 问题2:String Copy的时候 为什么不能像手动 .clone() 样的效果呢,数据一起复制出来一份 ??
- 1. Copy 不是“自动调用 clone()”
- 2. String 的 clone() 做了什么?
- 3. Copy 做的是什么?
- 4. 如果 String 的 Copy 等于自动 clone,会有什么问题?
- 5. 一个更明显的例子
- 6. Rust 的设计原则:昂贵操作必须显式
- 7. Copy 是一种承诺
- 8. Copy 不能写自定义复制逻辑
- 9. String 直接按位复制为什么不行?
- 10. Rust 为什么不把 Copy 改成“深拷贝”?
- 11. 为什么 Java / Go 好像可以?
- 12. 含 String 的 struct 为什么也是一样?
- 13. 那什么时候用 clone,什么时候用引用?
- 14. 可以类比成这三种情况
- 15. 总结
问题1:Rust Copy、Clone 等 derive 的使用说明,请详细举例 ?
核心原因一句话:
String 拥有堆内存资源,需要在变量生命周期结束时释放内存;如果 String 可以 Copy,就会产生多个对象同时拥有同一块堆内存,最终可能重复释放,所以 Rust 禁止 String 实现 Copy。
1. 先看一个含 String 字段的结构体
#[derive(Debug, Clone)]
struct User {
id: u64,
name: String,
}
fn main() {
let user1 = User {
id: 1,
name: String::from("Alice"),
};
let user2 = user1.clone();
println!("user1 = {:?}", user1);
println!("user2 = {:?}", user2);
}
这可以正常运行。
但是如果你写成:
#[derive(Debug, Clone, Copy)]
struct User {
id: u64,
name: String,
}
会报错。
因为:
String 不是 Copy 所以包含 String 字段的 User 也不能 Copy
Rust 的规则是:
一个 struct 能不能 Copy,取决于它的所有字段是否都能 Copy。 只要有一个字段不能 Copy,整个 struct 就不能 Copy。
2. 为什么 String 不能 Copy?
先看 String 内部大致长什么样。
一个 String 不是直接把字符串内容放在栈上,而是由三部分组成:
String ├── ptr 指向堆内存中真正的字符串内容 ├── len 当前字符串长度 └── capacity 当前容量
例如:
let s1 = String::from("hello");
内存可以简单理解为:
栈上 s1 ┌────────────┐ │ ptr ───────┼────> 堆内存 "hello" │ len = 5 │ │ cap = 5 │ └────────────┘
String 本身在栈上,但真正的 "hello" 内容在堆上。
3. 如果 String 可以 Copy,会发生什么?
假设 Rust 允许这样:
let s1 = String::from("hello");
let s2 = s1;
如果这是 Copy,就会变成:
栈上 s1
┌────────────┐
│ ptr ───────┼────┐
│ len = 5 │ │
│ cap = 5 │ │
└────────────┘ │
│
├──> 堆内存 "hello"
│
栈上 s2 │
┌────────────┐ │
│ ptr ───────┼────┘
│ len = 5 │
│ cap = 5 │
└────────────┘
这就出问题了。
因为 s1 和 s2 都认为自己拥有这块堆内存。
程序结束时,Rust 会自动释放变量占用的资源:
释放 s1 指向的堆内存 释放 s2 指向的堆内存
同一块内存被释放两次,这叫 double free,是非常严重的内存安全问题。
所以 Rust 直接从类型系统层面禁止:
String 不能 Copy 包含 String 的 struct 也不能 Copy
4. Rust 实际怎么处理?
Rust 不允许 String 自动 Copy,而是默认执行 move。
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{}", s2);
// println!("{}", s1); // 报错
}
这里:
let s2 = s1;
不是复制,而是移动所有权。
可以理解为:
原来: s1 拥有 "hello" 执行 let s2 = s1 之后: s2 拥有 "hello" s1 失效
所以 Rust 不允许你继续使用 s1。
这样可以保证最终只有一个变量负责释放堆内存。
5. 含 String 字段的 struct 也是一样
#[derive(Debug)]
struct User {
id: u64,
name: String,
}
fn main() {
let user1 = User {
id: 1,
name: String::from("Alice"),
};
let user2 = user1;
println!("{:?}", user2);
// println!("{:?}", user1); // 报错
}
执行:
let user2 = user1;
不是复制,而是移动。
因为 User 里面有 String,所以 User 不是 Copy 类型。
移动后:
user1 失效 user2 获得所有权
6. 那为什么 Clone 可以?
因为 Clone 是显式复制一份新的数据。
例如:
#[derive(Debug, Clone)]
struct User {
id: u64,
name: String,
}
fn main() {
let user1 = User {
id: 1,
name: String::from("Alice"),
};
let user2 = user1.clone();
println!("user1 = {:?}", user1);
println!("user2 = {:?}", user2);
}
这里调用:
user1.clone()
会创建一个新的 User。
其中:
id 是 u64,可以直接复制 name 是 String,会重新复制一份字符串内容
内存可以理解为:
user1.name ───> 堆内存 "Alice" user2.name ───> 另一块堆内存 "Alice"
所以 user1 和 user2 分别拥有不同的堆内存。
程序结束时:
释放 user1.name 的堆内存 释放 user2.name 的堆内存
不会重复释放同一块内存。
7. Copy 和 Clone 的本质区别
Copy:自动复制,不能自定义复杂逻辑
let b = a;
如果 a 是 Copy 类型,Rust 自动复制。
例如:
fn main() {
let a = 10;
let b = a;
println!("{}", a);
println!("{}", b);
}
i32 很小,没有堆内存,没有释放逻辑,所以可以 Copy。
Clone:显式复制,可以有成本
let b = a.clone();
例如:
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{}", s1);
println!("{}", s2);
}
clone() 对 String 来说,会重新分配堆内存并复制字符串内容。
所以 Rust 要你显式写出来:
.clone()
这样你能意识到这里可能有性能成本。
8. 为什么 Rust 不让 Copy 做深拷贝?
你可能会想:
那 Rust 为什么不让 String 的 Copy 自动变成深拷贝?
比如:
let s2 = s1;
自动复制一份新的堆内存,不就安全了吗?
Rust 不这样设计,原因有几个。
8.1 Copy 必须是便宜、简单、可预测的
Copy 的含义是:
像复制数字一样,简单、快速、隐式地复制。
例如:
let a = 1; let b = a;
这很轻量。
但如果 String 也可以 Copy,下面这行代码可能会偷偷分配内存:
let s2 = s1;
这会让性能变得不透明。
Rust 的原则是:
可能昂贵的操作,必须显式写出来。
所以复制 String 必须写:
let s2 = s1.clone();
8.2 Copy 不能自定义逻辑
Copy 是一种简单的按位复制。
它不会调用你自定义的复制逻辑。
而 Clone 可以自定义复制逻辑。
例如:
#[derive(Debug)]
struct User {
id: u64,
name: String,
}
impl Clone for User {
fn clone(&self) -> Self {
Self {
id: self.id,
name: self.name.clone(),
}
}
}
Clone 可以明确告诉 Rust:
name 字段需要调用 String 的 clone()
但是 Copy 不做这种复杂逻辑。
9. Copy 和 Drop 不能共存
String 是需要释放资源的类型,它内部实现了类似释放堆内存的逻辑。
在 Rust 中,这类释放逻辑来自 Drop。
你可以理解为:
String 生命周期结束时,会自动调用 Drop,释放堆内存。
而 Copy 类型不能实现 Drop。
为什么?
因为如果一个类型是 Copy,它可能被偷偷复制很多份。
let a = value; let b = a; let c = a; let d = a;
如果这些值都需要释放同一个资源,就非常危险。
所以 Rust 规定:
实现了 Drop 的类型不能 Copy。
String 需要 Drop,因此不能 Copy。
10. 对比:纯数字字段的 struct 可以 Copy
#[derive(Debug, Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 10, y: 20 };
let p2 = p1;
println!("p1 = {:?}", p1);
println!("p2 = {:?}", p2);
}
这里可以。
因为 Point 里面只有:
i32 i32
它们都是 Copy 类型。
内存上只是复制两个整数,没有堆内存,没有资源释放问题。
11. 对比:含 String 字段的 struct 不能 Copy
#[derive(Debug, Clone)]
struct User {
id: u64,
name: String,
}
fn main() {
let u1 = User {
id: 1,
name: String::from("Alice"),
};
let u2 = u1;
println!("{:?}", u2);
// println!("{:?}", u1); // 报错
}
因为 name: String 不是 Copy。
如果你想继续使用 u1,就用:
let u2 = u1.clone();
完整示例:
#[derive(Debug, Clone)]
struct User {
id: u64,
name: String,
}
fn main() {
let u1 = User {
id: 1,
name: String::from("Alice"),
};
let u2 = u1.clone();
println!("u1 = {:?}", u1);
println!("u2 = {:?}", u2);
}
12. 如果字段是 &str,能不能 Copy?
可以,但要理解区别。
#[derive(Debug, Clone, Copy)]
struct User {
id: u64,
name: &'static str,
}
fn main() {
let u1 = User {
id: 1,
name: "Alice",
};
let u2 = u1;
println!("{:?}", u1);
println!("{:?}", u2);
}
这个可以 Copy。
因为 &str 是引用。
复制引用只是复制一个地址和长度,不复制字符串内容本身。
可以理解为:
u1.name ─┐
├──> 静态字符串 "Alice"
u2.name ─┘
但是注意:
String
是拥有者。
&str
是借用者。
区别很大。
13. String 和 &str 的关键区别
| 类型 | 是否拥有数据 | 是否需要释放 | 是否 Copy |
|---|---|---|---|
String |
拥有字符串数据 | 需要 | 否 |
&str |
借用字符串数据 | 不需要 | 是 |
&String |
借用 String | 不需要 | 是 |
u64 |
直接存值 | 不需要 | 是 |
所以这个不能 Copy:
#[derive(Debug, Clone, Copy)]
struct User {
name: String,
}
这个可以 Copy:
#[derive(Debug, Clone, Copy)]
struct UserRef<'a> {
name: &'a str,
}
示例:
#[derive(Debug, Clone, Copy)]
struct UserRef<'a> {
id: u64,
name: &'a str,
}
fn main() {
let name = String::from("Alice");
let u1 = UserRef {
id: 1,
name: &name,
};
let u2 = u1;
println!("{:?}", u1);
println!("{:?}", u2);
}
这里复制的是引用,不是复制 String 本体。
14. 业务对象为什么一般不建议 Copy?
例如:
#[derive(Debug, Clone)]
struct Role {
id: u64,
code: String,
name: String,
permissions: Vec<String>,
}
这个类型包含:
String String Vec<String>
这些都涉及堆内存。
如果允许它自动 Copy,下面这行代码可能非常昂贵:
let role2 = role1;
你看不出来它复制了多少权限、多少字符串、多少堆内存。
所以 Rust 要求你显式写:
let role2 = role1.clone();
这样你能清楚知道:
这里正在复制一个完整的 Role 对象,可能有成本。
15. 更推荐的写法:读取时传引用,不要随便 clone
很多初学者遇到所有权报错,会直接 .clone()。
例如:
#[derive(Debug, Clone)]
struct User {
id: u64,
name: String,
}
fn print_user(user: User) {
println!("{:?}", user);
}
fn main() {
let user = User {
id: 1,
name: String::from("Alice"),
};
print_user(user.clone());
println!("{:?}", user);
}
这能运行,但不一定好。
如果函数只是读取用户信息,更推荐传引用:
#[derive(Debug)]
struct User {
id: u64,
name: String,
}
fn print_user(user: &User) {
println!("{:?}", user);
}
fn main() {
let user = User {
id: 1,
name: String::from("Alice"),
};
print_user(&user);
println!("{:?}", user);
}
这样不会复制 String,性能更好。
16. 函数传参中的区别
16.1 Copy 类型传参
#[derive(Debug, Clone, Copy)]
struct UserId(u64);
fn print_user_id(id: UserId) {
println!("{:?}", id);
}
fn main() {
let id = UserId(1001);
print_user_id(id);
println!("{:?}", id);
}
id 传入函数后仍然能用。
因为 UserId 是 Copy。
16.2 含 String 的 struct 传参
#[derive(Debug)]
struct User {
id: u64,
name: String,
}
fn print_user(user: User) {
println!("{:?}", user);
}
fn main() {
let user = User {
id: 1,
name: String::from("Alice"),
};
print_user(user);
// println!("{:?}", user); // 报错
}
这里 user 被移动进函数了。
因为 User 不是 Copy。
如果只是读取,应该改成:
fn print_user(user: &User) {
println!("{:?}", user);
}
调用:
print_user(&user);
17. 实战设计建议
17.1 适合 Copy 的类型
这些通常可以 Copy:
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct UserId(u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct RoleId(u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum UserStatus {
Enabled,
Disabled,
Locked,
}
它们特点是:
小 简单 没有堆内存 没有资源释放逻辑 复制成本极低
17.2 适合 Clone 但不适合 Copy 的类型
#[derive(Debug, Clone)]
struct User {
id: UserId,
username: String,
nickname: String,
status: UserStatus,
}
#[derive(Debug, Clone)]
struct Role {
id: RoleId,
code: String,
name: String,
}
它们特点是:
包含 String、Vec、HashMap 等堆内存字段 复制可能有成本 不能自动 Copy 需要显式 clone()
18. 总结
含有 String 字段的 struct 只能 Clone,不能 Copy,根本原因是:
String 拥有堆内存 String 生命周期结束时需要释放堆内存 Copy 是隐式按位复制 如果 String 可以 Copy,会导致多个变量指向同一块堆内存 最终可能重复释放,造成内存安全问题 所以 Rust 禁止 String 实现 Copy 因此包含 String 的 struct 也不能 Copy
最常见的判断规则:
字段都是 i32、u64、bool、char、简单 enum:可以 Copy 字段里有 String、Vec、HashMap、Box、文件句柄、数据库连接:不能 Copy,最多 Clone 只是读取数据:优先传 &T 确实需要一份独立数据:使用 .clone()
一个合理的业务写法:
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct UserId(u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum UserStatus {
Enabled,
Disabled,
Locked,
}
#[derive(Debug, Clone)]
struct User {
id: UserId,
username: String,
nickname: String,
status: UserStatus,
}
这里:
UserId 是数字包装,适合 Copy UserStatus 是简单枚举,适合 Copy User 包含 String,不适合 Copy,只适合 Clone
问题2:String Copy的时候 为什么不能像手动 .clone() 样的效果呢,数据一起复制出来一份 ??
不能的核心原因是:
Copy 的语义就是“像复制整数一样,隐式、廉价、按位复制”。 而 String::clone() 的语义是“显式分配一块新堆内存,然后把字符串内容复制过去”。
这两个语义完全不一样。
1. Copy 不是“自动调用 clone()”
很多初学者容易这样想:
let s2 = s1;
能不能让 Rust 自动变成:
let s2 = s1.clone();
对于 String 来说,这样看起来确实很方便。
但 Rust 故意不这么设计。
因为在 Rust 里:
let b = a;
对于 Copy 类型,含义是:
直接复制栈上的那几个字节
而不是:
调用某个函数,分配内存,再复制内容
2. String 的 clone() 做了什么?
例如:
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{}", s1);
println!("{}", s2);
}
String::clone() 大概做了这些事:
1. 读取 s1 的字符串内容 2. 在堆上重新申请一块内存 3. 把 "hello" 的字节复制到新内存中 4. 创建一个新的 String 5. 让 s2 拥有这块新内存
内存效果类似:
s1 ───> 堆内存 A:"hello" s2 ───> 堆内存 B:"hello"
这是安全的。
但是这不是简单复制,它有明显成本:
要分配内存 要复制内容 字符串越大,成本越高
3. Copy 做的是什么?
例如:
fn main() {
let a = 10;
let b = a;
println!("{}", a);
println!("{}", b);
}
i32 是 Copy。
它的复制可以理解为:
把 a 栈上的 4 个字节复制给 b
非常简单。
再看结构体:
#[derive(Debug, Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 10, y: 20 };
let p2 = p1;
println!("{:?}", p1);
println!("{:?}", p2);
}
Point 里面只有两个 i32。
所以:
let p2 = p1;
只是复制两个整数。
没有分配内存,没有释放资源,没有复杂逻辑。
4. 如果 String 的 Copy 等于自动 clone,会有什么问题?
假设 Rust 允许:
let s1 = String::from("hello");
let s2 = s1; // 自动深拷贝
看起来很好。
但是问题在于:Rust 中很多地方都会发生“看起来像普通传递”的操作。
例如函数传参:
fn print_name(name: String) {
println!("{}", name);
}
fn main() {
let name = String::from("Alice");
print_name(name);
println!("{}", name);
}
如果 String 是 Copy,那 print_name(name) 就会偷偷复制一份完整字符串。
等价于:
print_name(name.clone());
这就很危险了。
因为你看代码的时候,以为只是传了一个变量:
print_name(name);
但背后可能做了:
分配内存 复制字符串 构造新 String 释放旧 String
Rust 不希望昂贵操作偷偷发生。
5. 一个更明显的例子
假设有一个很大的字符串:
fn handle_log(log: String) {
println!("{}", log.len());
}
fn main() {
let log = "A".repeat(100_000_000);
handle_log(log);
println!("{}", log.len());
}
如果 String 可以像 Copy 一样自动 clone,那么这一句:
handle_log(log);
可能会偷偷复制 100MB 数据。
如果在循环里:
fn handle_log(log: String) {
println!("{}", log.len());
}
fn main() {
let log = "A".repeat(100_000_000);
for _ in 0..10 {
handle_log(log);
}
}
如果 String 是 Copy = 自动 clone,这段代码可能会偷偷复制 10 次 100MB 数据。
也就是:
复制 1GB 数据 多次堆内存分配 性能灾难
所以 Rust 要求你显式写:
handle_log(log.clone());
这样你一眼就知道:
这里正在复制数据,可能有成本
6. Rust 的设计原则:昂贵操作必须显式
Rust 很强调这一点:
便宜、简单、无副作用的复制:可以 Copy 可能昂贵、可能分配内存的复制:必须 clone()
所以:
let a = 10; let b = a;
可以自动复制。
但:
let s1 = String::from("hello");
let s2 = s1.clone();
必须显式 .clone()。
这样代码的性能成本更清楚。
7. Copy 是一种承诺
一个类型实现了 Copy,等于告诉别人:
这个类型可以随便复制 复制成本很低 复制不会分配资源 复制不会执行复杂逻辑 复制后原值还能继续用
例如:
#[derive(Debug, Clone, Copy)] struct UserId(u64);
这个很合理。
fn query_user(id: UserId) {
println!("{:?}", id);
}
fn main() {
let id = UserId(1001);
query_user(id);
println!("{:?}", id);
}
UserId 只是一个 u64 包装,复制它没问题。
但如果是:
struct User {
id: u64,
name: String,
}
如果它也能 Copy,那复制 User 就可能复制 String 堆内存。
这不是一个“低成本复制”的类型。
8. Copy 不能写自定义复制逻辑
Clone 可以自定义:
#[derive(Debug)]
struct User {
id: u64,
name: String,
}
impl Clone for User {
fn clone(&self) -> Self {
Self {
id: self.id,
name: self.name.clone(),
}
}
}
这里可以明确写:
name: self.name.clone()
也就是复制一份新的字符串内容。
但是 Copy 不是这样。
Copy 基本上是告诉编译器:
这个类型直接按位复制就行
你不能让 Copy 变成:
复制时执行一堆自定义逻辑 复制时申请内存 复制时调用函数
如果要这些行为,就应该用 Clone。
9. String 直接按位复制为什么不行?
String 内部大概是:
String {
ptr,
len,
capacity,
}
例如:
let s1 = String::from("hello");
内存类似:
栈上 s1 ┌────────────┐ │ ptr ───────┼────> 堆内存 "hello" │ len = 5 │ │ cap = 5 │ └────────────┘
如果对 String 做 Copy 的那种按位复制,就会得到:
栈上 s1
┌────────────┐
│ ptr ───────┼────┐
│ len = 5 │ │
│ cap = 5 │ │
└────────────┘ │
├──> 堆内存 "hello"
栈上 s2 │
┌────────────┐ │
│ ptr ───────┼────┘
│ len = 5 │
│ cap = 5 │
└────────────┘
这就变成:
s1 和 s2 都认为自己拥有同一块堆内存
程序结束时:
s1 释放一次 s2 再释放一次
同一块内存被释放两次,会出严重问题。
所以 String 不能 Copy。
10. Rust 为什么不把 Copy 改成“深拷贝”?
因为那样 Copy 就失去了本来的意义。
现在 Rust 的规则很清楚:
Copy = 简单按位复制 Clone = 显式复制,可能有复杂逻辑
如果让 Copy 自动深拷贝,那么:
let b = a;
这行代码就可能有两种完全不同的成本:
复制 i32:几乎没有成本 复制 String:分配内存 + 复制字符串 复制 Vec<User>:可能复制大量对象 复制 HashMap:可能复制整张表
代码表面看起来都一样:
let b = a;
但背后的成本完全不可预测。
Rust 不喜欢这种隐式成本。
11. 为什么 Java / Go 好像可以?
有些语言里对象变量赋值看起来很方便:
User u2 = u1;
但这通常不是深拷贝,而是复制引用。
也就是说:
u1 ─┐
├──> 同一个 User 对象
u2 ─┘
Rust 不是这种默认模型。
Rust 的 String 是明确的资源所有者。
它要保证:
一块资源同一时间只有一个明确所有者
所以:
let s2 = s1;
表示所有权移动。
如果你真的要复制数据,必须写:
let s2 = s1.clone();
12. 含 String 的 struct 为什么也是一样?
例如:
#[derive(Debug, Clone)]
struct User {
id: u64,
name: String,
}
这个结构体里:
id: u64 可以 Copy name: String 不能 Copy
因为只要有一个字段不能 Copy,整个结构体就不能 Copy。
如果你写:
#[derive(Debug, Clone, Copy)]
struct User {
id: u64,
name: String,
}
会报错。
原因是:
User 如果 Copy,就要求 name 也 Copy 但 String 不能 Copy 所以 User 不能 Copy
正确写法:
#[derive(Debug, Clone)]
struct User {
id: u64,
name: String,
}
使用时:
fn main() {
let user1 = User {
id: 1,
name: String::from("Alice"),
};
let user2 = user1.clone();
println!("{:?}", user1);
println!("{:?}", user2);
}
13. 那什么时候用 clone,什么时候用引用?
不要为了绕过所有权就到处 .clone()。
如果只是读取,应该传引用:
#[derive(Debug)]
struct User {
id: u64,
name: String,
}
fn print_user(user: &User) {
println!("{:?}", user);
}
fn main() {
let user = User {
id: 1,
name: String::from("Alice"),
};
print_user(&user);
println!("{:?}", user);
}
这里没有复制 User,也没有复制 String。
如果你确实需要一份独立的数据,再用:
let user2 = user1.clone();
14. 可以类比成这三种情况
情况一:移动所有权
let s2 = s1;
含义:
s1 把字符串所有权交给 s2 s1 失效 没有复制堆数据
成本低。
情况二:显式 clone
let s2 = s1.clone();
含义:
s1 还在 s2 是一份新的 String 堆数据复制了一份
安全,但有成本。
情况三:借用
let r = &s1;
含义:
r 只是借用 s1 不拥有数据 不复制数据
成本最低,但有生命周期限制。
15. 总结
String 的 Copy 不能设计成自动 .clone() 效果,主要原因是:
1. Copy 的语义是隐式、简单、按位复制 2. Clone 的语义是显式、可自定义、可能有成本的复制 3. String::clone() 需要分配堆内存并复制内容 4. Rust 不希望昂贵操作在 let b = a、函数传参、循环中偷偷发生 5. Copy 类型通常要求复制便宜、无资源管理、无 Drop 6. String 拥有堆内存,需要 Drop,所以不能 Copy
最重要的一句话:
Rust 不是做不到让 String 自动 clone, 而是故意不这么设计。 因为 Rust 要让“可能昂贵的复制”显式暴露出来。
所以:
let s2 = s1;
表示移动所有权。
let s2 = s1.clone();
表示明确复制一份新数据。
let r = &s1;
表示只借用,不复制。