上一篇说赋值会移动,但 let b = a 对 i32 就没有移动 —— 因为它是 Copy。这篇把「哪些类型是 Copy」直接编译出来,再验证 Copy 的两个限制(必须带 Clone、不能有 Drop)和 Copy 的真实代价:它就是一次按位复制。
源码:
1 | fn main() {
2 | let a = 5;
3 | let b = a;
4 | println!("a = {a}, b = {b}");
5 | let p = (1, 2.5);
6 | let q = p;
7 | println!("p = {:?}, q = {:?}", p, q);
8 | }
编译:成功
运行(退出码 0):
a = 5, b = 5
p = (1, 2.5), q = (1, 2.5)类型 是 Copy 吗 i32 是 f64 是 bool 是 char 是 () 是 &i32 是 &mut i32 否 (i32, i32) 是 (i32, String) 否 [i32; 3] 是 [String; 1] 否 String 否 Vec<i32> 否 Box<i32> 否 Option<i32> 是 Option<String> 否 std::rc::Rc<i32> 否 fn(i32) -> i32 是 std::cell::Cell<i32> 否 &str 是 std::ops::Range<i32> 否
源码:
1 | #[derive(Copy)]
2 | struct P { x: i32 }
3 | fn main() {}
编译:失败
error[E0277]: the trait bound `P: Clone` is not satisfied
--> <实验目录>/src/u17b.rs:2:8
|
1 | #[derive(Copy)]
| ---- in this derive macro expansion
2 | struct P { x: i32 }
| ^ the trait `Clone` is not implemented for `P`
|
note: required by a bound in `Copy`
--> <rust 标准库源码>/library/core/src/marker.rs:454:17
|
454 | pub trait Copy: Clone {
| ^^^^^ required by this bound in `Copy`
help: consider annotating `P` with `#[derive(Clone)]`
|
2 + #[derive(Clone)]
3 | struct P { x: i32 }
|
…(共 22 行)
源码:
1 | #[derive(Clone, Copy)]
2 | struct P { x: i32 }
3 | impl Drop for P { fn drop(&mut self) {} }
4 | fn main() {}
编译:失败
error[E0184]: the trait `Copy` cannot be implemented for this type; the type has a destructor
--> <实验目录>/src/u17c.rs:2:8
|
1 | #[derive(Clone, Copy)]
| ---- in this derive macro expansion
2 | struct P { x: i32 }
| ^ `Copy` not allowed on types with destructors
|
note: destructor declared here
--> <实验目录>/src/u17c.rs:3:19
|
3 | impl Drop for P { fn drop(&mut self) {} }
| ^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0184`. 源码:
1 | use std::hint::black_box;
2 | use std::time::Instant;
3 | fn main() {
4 | for &n in &[16usize, 4096, 1 << 20] {
5 | macro_rules! bench { ($N:expr) => {{
6 | let a = black_box([7u8; $N]);
7 | let t = Instant::now();
8 | let mut acc = 0u64;
9 | for _ in 0..2000 { let b = black_box(a); acc += b[0] as u64; }
10 | black_box(acc);
11 | t.elapsed().as_nanos() as f64 / 2000.0
12 | }} }
13 | let ns = match n { 16 => bench!(16), 4096 => bench!(4096), _ => bench!(1048576) };
14 | println!("[u8; {:>7}] 每次赋值(拷贝)约 {:>9.0} ns", n, ns);
15 | }
16 | }
编译:成功
运行(退出码 0):
[u8; 16] 每次赋值(拷贝)约 1 ns
[u8; 4096] 每次赋值(拷贝)约 129 ns
[u8; 1048576] 每次赋值(拷贝)约 20866 ns计时用 std::hint::black_box 防止编译器把复制优化掉,数字是单次运行的结果;小到 1 ns 的值已经接近计时本身的精度。