稍微写过一点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 0x7ffee3f839b0ptr_b 0x7ffee3f83a18ref_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 0x7ffee6d76950ptr_b 0x7ffee6d769d8ref_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的变量而言,只是告诉编译器你不可以再访问它了。
