最近因为疫情问题,2020 年到了2月下旬才开始复工。上班在地铁上又恢复了以往的看“微信读书”的习惯。依旧还是啃着去年没有啃完的《深入浅出Rust》。
读到“第二部分”的第11章 —— 所有权和移动语义。提到 Rust 中 move 语义是默认的,用一段代码直观的看一下:
#[derive(Debug)]
struct Point {
x_value: i32,
y_value: i32,
}
pub fn no_copy() {
let v1 = Point {x_value: 1, y_value: 2};
let v2 = v1;
println!("{:?}", v1);
}
当我们用 cargo run
执行代码时,编译器会提示:
borrow of moved value: `v1`
也就是执行 let v2 = v1;
语句时,会将 v1
的所有权转移给 v2
。之后,v1
就会被 drop 掉。
呐,这就是前面所提到的「Rust 中 move 语义是默认的」。move 的意思就是转移所有权。当然,不是所有的类型都是 move 的,对于诸如 u8
、i32
、f16
这种基础类型在你赋值时是不会 move 的。那么有没有办法,让那些复杂或者用户自定义类型也能像其他语言一样,将一个变量赋值给另一个变量,并且不会转义所有权呢?
答案是有的。你只要为那个目标类型实现 Copy
trait。接下来,我们为上方的 Point
类型实现 Copy trait。
#[derive(Debug)]
struct Point2 {
x_value: i32,
y_value: i32,
}
impl Copy for Point2 {
}
impl Clone for Point2{
fn clone(&self) -> Point2 {
*self
}
}
pub fn has_copy() {
let v1 = Point2 {
x_value: 1,
y_value: 2,
};
let mut v2 = v1;
println!("{:?}", v1);
v2.x_value += 1;
println!("{:?}", v1);
println!("{:?}", v2);
}
/// 输出
/// ```
/// Point2 { x_value: 1, y_value: 2 }
/// Point2 { x_value: 1, y_value: 2 }
/// Point2 { x_value: 2, y_value: 2 }
/// ```
通过上面的例子,可以看出,为 Point2 实现 Copy
、Clone
trait,即可让自定义类型自由地赋值,并且不会转移所有权。可不幸的是,不是所有的类型都能 Copy 和 Clone 的。下面是书中对 Copy 的介绍:
Copy的全名是std::marker::Copy。请大家注意,std::marker模块里面所有的trait都是特殊的trait。目前稳定的有四个,它们是Copy、Send、Sized、Sync。它们的特殊之处在于:它们是跟编译器密切绑定的,impl这些trait对编译器的行为有重要影响。在编译器眼里,它们与其他的trait不一样。这几个trait内部都没有方法,它们的唯一任务是给类型打一个“标记”,表明它符合某种约定——这些约定会影响编译器的静态检查以及代码生成。
Copy这个trait在编译器的眼里代表的是什么意思呢?简单点总结就是,如果一个类型impl了Copy trait,意味着任何时候,我们都可以通过简单的内存复制(在C语言里按字节复制memcpy)实现该类型的复制,并且不会产生任何内存安全问题。 -- 《深入浅出Rust》