稍微写过一点rust的人就知道,和别的大多数语言不同,rust默认的赋值语义是move,其他语言例如golang,默认的语义是copy,例如
type Hello struct {
Name: string,
}
h := Hello {
Name: "hello",
}
h_copy := h
h_copy 是 h的copy,而rust不同
struct Hello {
int_field: i32,
string_field: String,
}
let a = Hello {
int_field: 1,
string_field: String::from("hello"),
};
let b = a;
这时候我们说 a move 到了b, 几乎所有的书以及文档都告诉你,这时候 Hello的ownership转移到了b上,a “不复存在”,那到底move做了什么?为了搞清楚这个,我写了2段测试代码
fn move_vec() {
let mut a = vec![1,3,4];
let ptr_a = &mut a as *mut Vec<i32>;
println!("ptr_a {:?}", ptr_a);
let mut b = a;
let ptr_b = &mut b as *mut Vec<i32>;
println!("ptr_b {:?}", ptr_b);
b.push(5);
b.push(6);
b.push(7);
b.push(8);
println!("ref_b {:?}", &b);
unsafe{
let a = &*ptr_a;
println!("ref_a {:?}", &a);
}
}
输出为
ptr_a 0x7ffee3f839b0
ptr_b 0x7ffee3f83a18
ref_b [1, 3, 4, 5, 6, 7, 8]
ref_a [1, 3, 4]
可以看到,当 b = a 后,a和b的指针地址发生了变化,我们指到vec实际上是一个fat pointer,一个指向back array的指针,以及2个字段,一个保存长度,一个保存capacity,所以当move发生后,这个fatpointer实际上发生了copy,指向backarray的指针没有变,当b发生修改后,capacity以及len都变大了,但原始vec没有变。
[derive(Debug)]
struct Hello {
int_field: i32,
string_field: String,
}
fn move_struct() {
let mut a = Hello {
int_field: 1,
string_field: String::from("hello"),
};
let ptr_a = &mut a as *mut Hello;
println!("ptr_a {:?}", ptr_a);
let mut b = a;
let ptr_b = &mut b as *mut Hello;
println!("ptr_b {:?}", ptr_b);
b.int_field = 2;
b.string_field = String::from("world");
println!("ref_b {:?}", &b);
unsafe{
let a = &*ptr_a;
println!("ref_a {:?}", &a);
}
}
输出
ptr_a 0x7ffee6d76950
ptr_b 0x7ffee6d769d8
ref_b Hello { int_field: 2, string_field: "world" }
ref_a Hello { int_field: 1, string_field: "hello" }
Hello在栈上分配,当发生move后,可以看到b是a的copy,这时候a和b完全没有关系了,a”不再存在”只是对于编译器而言,然而当你通过unsafe去访问a的裸指针时,你任然可以访问到它,但是必须通过unsafe,rust不再保证安全性
所以rust中,move实际上就是一次memcpy,对于被move的变量而言,只是告诉编译器你不可以再访问它了。