Rust 铁锈中的移动语义是什么?

Rust 铁锈中的移动语义是什么?,rust,move-semantics,ownership,Rust,Move Semantics,Ownership,在Rust中,有两种可能引用 借用,即获取引用,但不允许对引用目的地进行变异。&运算符从值借用所有权 可变借用,即引用以改变目的地。&mut运算符可变地从值借用所有权 报告说: 首先,任何借贷的持续时间不得超过 主人。第二,你可能有这两种类型中的一种或另一种 借款,但不能同时借款: 对资源的一个或多个引用(&T) 只有一个可变引用(&mut T) 我相信引用就是创建一个指向该值的指针,并通过指针访问该值。如果有更简单的等效实现,编译器可以对此进行优化 但是,我不理解移动的含义以及它是如何实现

在Rust中,有两种可能引用

  • 借用,即获取引用,但不允许对引用目的地进行变异。
    &
    运算符从值借用所有权

  • 可变借用,即引用以改变目的地。
    &mut
    运算符可变地从值借用所有权

  • 报告说:

    首先,任何借贷的持续时间不得超过 主人。第二,你可能有这两种类型中的一种或另一种 借款,但不能同时借款:

    • 对资源的一个或多个引用(
      &T
    • 只有一个可变引用(
      &mut T
    我相信引用就是创建一个指向该值的指针,并通过指针访问该值。如果有更简单的等效实现,编译器可以对此进行优化

    但是,我不理解移动的含义以及它是如何实现的

    对于实现
    Copy
    特征的类型,它意味着复制,例如通过从源中按顺序分配结构成员,或通过
    memcpy()
    。对于小型结构或基本体,此副本是有效的

    对于移动

    这个问题并不是复制的,因为Rub和C++是不同的语言,移动语义在两者之间是不同的。

    < P>当你移动一个项目时,你正在转移该项目的所有权。这是生锈的关键成分

    假设我有一个结构,然后我将结构从一个变量分配到另一个变量。默认情况下,这将是一个移动,我已经转移了所有权。编译器将跟踪此所有权更改,并阻止我再使用旧变量:

    pub struct Foo {
        value: u8,
    }
    
    fn main() {
        let foo = Foo { value: 42 };
        let bar = foo;
    
        println!("{}", foo.value); // error: use of moved value: `foo.value`
        println!("{}", bar.value);
    }
    
    它是如何实施的

    从概念上讲,移动某物不需要做任何事情。在上面的例子中,当我分配给不同的变量时,没有理由在某个地方实际分配空间,然后移动分配的数据。我实际上不知道编译器是做什么的,它可能会根据优化的级别而改变

    但出于实际目的,您可以认为,当您移动某个对象时,表示该对象的位会被复制,就像通过
    memcpy
    一样。这有助于解释当您将变量传递给使用它的函数时,或者当您从函数返回值时会发生什么(同样,优化器可以做其他事情来提高效率,这只是概念上的):

    “但是等等!”,你说,“
    memcpy
    只在实现
    Copy
    !”的类型中起作用。这在很大程度上是正确的,但最大的区别在于,当一个类型实现
    Copy
    时,源和目标都可以在复制后使用

    移动语义的一种思考方式与复制语义相同,但增加了一个限制,即被移动的对象不再是可使用的有效项

    然而,从另一个角度考虑它通常更容易:你能做的最基本的事情是转移/放弃所有权,而复制东西的能力是一种额外的特权。这就是锈菌塑造它的方式


    这对我来说是个棘手的问题!使用Rust一段时间后,移动语义是自然的。让我知道我遗漏了哪些部分或解释得不好。

    请让我回答我自己的问题。我遇到了麻烦,但在这里我问了一个问题。现在我明白了:

    移动是价值的所有权转移

    例如,赋值
    设x=a转移所有权:首先
    a
    拥有该值。在
    之后,让
    x
    拥有该值。锈蚀后禁止使用
    a

    事实上,如果你做了
    println!(“a:{:?}”,a)
    之后让
    Rust编译器说:

    error: use of moved value: `a`
    println!("a: {:?}", a);
                        ^
    
    完整示例:

    #[derive(Debug)]
    struct Example { member: i32 }
    
    fn main() {
        let a = Example { member: 42 }; // A struct is moved
        let x = a;
        println!("a: {:?}", a);
        println!("x: {:?}", x);
    }
    
    这个移动意味着什么


    这个概念似乎来自C++11。A说:

    从客户机代码的角度来看,选择“移动”而不是“复制”意味着您不关心源代码的状态会发生什么变化

    啊哈。C++11不关心源代码会发生什么。因此,在这种情况下,Rust可以在移动后自由决定禁止使用源

    它是如何实施的

    我不知道。但我可以想象锈实际上什么都没有
    x
    只是相同值的不同名称。名称通常被编译掉(当然调试符号除外)。因此,无论绑定的名称是
    a
    还是
    x
    ,机器代码都是相同的

    <>看起来C++在复制构造函数删除中也一样。


    什么都不做是最有效率的。将值传递给函数,也会导致所有权的转移;这与其他示例非常相似:

    struct Example { member: i32 }
    
    fn take(ex: Example) {
        // 2) Now ex is pointing to the data a was pointing to in main
        println!("a.member: {}", ex.member) 
        // 3) When ex goes of of scope so as the access to the data it 
        // was pointing to. So Rust frees that memory.
    }
    
    fn main() {
        let a = Example { member: 42 }; 
        take(a); // 1) The ownership is transfered to the function take
                 // 4) We can no longer use a to access the data it pointed to
    
        println!("a.member: {}", a.member);
    }
    
    因此,预期误差为:

    post_test_7.rs:12:30: 12:38 error: use of moved value: `a.member`
    

    语义

    锈迹是一种被称为:

    仿射类型是施加较弱约束的线性类型的一个版本,对应于仿射逻辑仿射资源只能使用一次,而线性资源必须使用一次

    不是
    Copy
    ,因此被移动的类型是仿射类型:您可以使用它们一次,也可以不使用它们,而不使用其他类型

    Rust在其以所有权为中心的世界观(*)中将此定义为所有权转让

    (*)一些研究锈病的人比我在CS中的资历要高得多,他们故意实施了仿射型系统;然而,与Haskell公开math-y/cs-y概念相反,Rust倾向于公开更多的实用概念

    注意:有人可能会认为,从标记为
    #[必须使用]
    的函数返回的仿射类型实际上是我阅读的线性类型


    实施post_test_7.rs:12:30: 12:38 error: use of moved value: `a.member`
    fn main() {
        let s = "Hello, World!".to_string();
        let t = s;
        println!("{}", t);
    }
    
    %_5 = alloca %"alloc::string::String", align 8
    %t = alloca %"alloc::string::String", align 8
    %s = alloca %"alloc::string::String", align 8
    
    %0 = bitcast %"alloc::string::String"* %s to i8*
    %1 = bitcast %"alloc::string::String"* %_5 to i8*
    call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* %0, i64 24, i32 8, i1 false)
    %2 = bitcast %"alloc::string::String"* %_5 to i8*
    %3 = bitcast %"alloc::string::String"* %t to i8*
    call void @llvm.memcpy.p0i8.p0i8.i64(i8* %3, i8* %2, i64 24, i32 8, i1 false)