稍微写过一点rust的人就知道,和别的大多数语言不同,rust默认的赋值语义是move,其他语言例如golang,默认的语义是copy,例如

    1. type Hello struct {
    2. Name: string,
    3. }
    4. h := Hello {
    5. Name: "hello",
    6. }
    7. h_copy := h

    h_copy 是 h的copy,而rust不同

    1. struct Hello {
    2. int_field: i32,
    3. string_field: String,
    4. }
    5. let a = Hello {
    6. int_field: 1,
    7. string_field: String::from("hello"),
    8. };
    9. let b = a;

    这时候我们说 a move 到了b, 几乎所有的书以及文档都告诉你,这时候 Hello的ownership转移到了b上,a “不复存在”,那到底move做了什么?为了搞清楚这个,我写了2段测试代码

    1. fn move_vec() {
    2. let mut a = vec![1,3,4];
    3. let ptr_a = &mut a as *mut Vec<i32>;
    4. println!("ptr_a {:?}", ptr_a);
    5. let mut b = a;
    6. let ptr_b = &mut b as *mut Vec<i32>;
    7. println!("ptr_b {:?}", ptr_b);
    8. b.push(5);
    9. b.push(6);
    10. b.push(7);
    11. b.push(8);
    12. println!("ref_b {:?}", &b);
    13. unsafe{
    14. let a = &*ptr_a;
    15. println!("ref_a {:?}", &a);
    16. }
    17. }
    18. 输出为
    19. ptr_a 0x7ffee3f839b0
    20. ptr_b 0x7ffee3f83a18
    21. ref_b [1, 3, 4, 5, 6, 7, 8]
    22. ref_a [1, 3, 4]

    可以看到,当 b = a 后,a和b的指针地址发生了变化,我们指到vec实际上是一个fat pointer,一个指向back array的指针,以及2个字段,一个保存长度,一个保存capacity,所以当move发生后,这个fatpointer实际上发生了copy,指向backarray的指针没有变,当b发生修改后,capacity以及len都变大了,但原始vec没有变。

    1. [derive(Debug)]
    2. struct Hello {
    3. int_field: i32,
    4. string_field: String,
    5. }
    6. fn move_struct() {
    7. let mut a = Hello {
    8. int_field: 1,
    9. string_field: String::from("hello"),
    10. };
    11. let ptr_a = &mut a as *mut Hello;
    12. println!("ptr_a {:?}", ptr_a);
    13. let mut b = a;
    14. let ptr_b = &mut b as *mut Hello;
    15. println!("ptr_b {:?}", ptr_b);
    16. b.int_field = 2;
    17. b.string_field = String::from("world");
    18. println!("ref_b {:?}", &b);
    19. unsafe{
    20. let a = &*ptr_a;
    21. println!("ref_a {:?}", &a);
    22. }
    23. }
    24. 输出
    25. ptr_a 0x7ffee6d76950
    26. ptr_b 0x7ffee6d769d8
    27. ref_b Hello { int_field: 2, string_field: "world" }
    28. 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的变量而言,只是告诉编译器你不可以再访问它了。