可变引用有移动语义吗? [英] Do mutable references have move semantics?

查看:102
本文介绍了可变引用有移动语义吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

fn main() {
    let mut name = String::from("Charlie");
    let x = &mut name;
    let y = x;       // x has been moved
    say_hello(y);
    say_hello(y);       // but y has not been moved, it is still usable
    change_string(y);
    change_string(y);  

}

fn say_hello(s: &str) {
    println!("Hello {}", s);
}

fn change_string(s: &mut String) {
    s.push_str(" Brown");
}

当我将 x 分配给 yx 已被移动.但是,当我在函数中使用它时,我希望可以移动具有移动语义的东西.但是,我仍然可以在后续调用后使用参考.也许这与 say_hello() 接受不可变引用有关,但 change_string() 接受可变引用但引用仍未移动.

When I assign x to y x has been moved. However, I would expect something with move semantics to be moved when I use it in a function. However, I can still use the reference after subsequent calls. Maybe this has to do with say_hello() taking a immutable reference but change_string() takes a mutable reference but the reference is still not moved.

推荐答案

你的推理和观察都完全正确.看起来事情肯定应该按照你描述的方式发生.然而,编译器在这里应用了一些便利魔法.

You are completely right with both your reasoning and your observations. It definitely looks like things should be happening the way you describe it. However, the compiler applies some convenience magic here.

移动语义在 Rust 中通常适用于所有未实现 Copy 特性的类型.共享引用是Copy,因此它们在分配或传递给函数时被简单地复制.可变引用不是Copy,所以它们应该被移动.

Move semantics generally apply in Rust for all types that do not implement the Copy trait. Shared references are Copy, so they are simply copied when assigned or passed to a function. Mutable references are not Copy, so they should be moved.

这就是魔法开始的地方.每当将可变引用分配给编译器已知为可变引用的类型的名称时,原始引用将隐式地重新借用而不是被移动.所以函数调用

That's where the magic starts. Whenever a mutable reference is assigned to a name with a type already known to be a mutable reference by the compiler, the original reference is implicitly reborrowed instead of being moved. So the function called

change_string(y);

被编译器转换成意思

change_string(&mut *y);

原始引用被取消引用,并创建了一个新的可变借用.这个新借位被移到函数中,一旦函数返回,原始借位就会被释放.

The original reference is derefenced, and a new mutable borrow is created. This new borrow is moved into the function, and the original borrow gets released once the function returns.

请注意,这不是函数调用和赋值之间的区别.每当编译器已知目标类型是可变引用时,就会发生隐式重新借用,例如因为模式有一个明确的类型注释.所以这一行还创建了一个隐式重新借用,因为我们明确地将其注释为可变引用类型:

Note that this isn't a difference between function calls and assignments. Implicit reborrows happen whenever the target type is already known to be a mutable reference by the compiler, e.g. because the pattern has an explicit type annotation. So this line also creates an implicit reborrow, since we explicitly annotated it as a mutable reference type:

let y: &mut _ = x;

另一方面,这个函数调用移动(从而消耗)可变引用y:

This function call on the other hand moves (and thus consumes) the mutable reference y:

fn foo<T>(_: T) {}

[...]
foo(y);

这里的泛型类型 T 不是明确的可变引用类型,因此不会发生隐式重新借用,即使编译器推断该类型是可变引用 - 就像您的情况一样赋值 let y = x;.

The generic type T here isn't explicitly a mutable reference type, so no implicit reborrow occurs, even though the compiler infers that the type is a mutable reference – just as in the case of your assignment let y = x;.

在某些情况下,即使没有显式类型注释,编译器也可以推断泛型类型是可变引用:

In some cases, the compiler can infer a generic type is a mutable reference even in the absence of an explicit type annotation:

fn bar<T>(_a: T, _b: T) {}

fn main() {
    let mut i = 42;
    let mut j = 43;
    let x = &mut i;
    let y = &mut j;
    bar(x, y);   // Moves x, but reborrows y.
    let _z = x;  // error[E0382]: use of moved value: `x`
    let _t = y;  // Works fine. 
}

在推断第一个参数的类型时,编译器还不知道它是一个可变引用,因此不会发生隐式重新借用并且 x 被移动到函数中.但是,当到达第二个参数时,编译器已经推断出T 是可变引用,因此隐式重新借用了y.(这个例子很好地说明了为什么添加编译器魔法来使事情正常工作"通常是一个坏主意.显式优于隐式.)

When inferring the type of the first parameter, the compiler doesn't know yet it's a mutable reference, so no implicit reborrow occurs and x is moved into the function. However, when reaching the second parameter, the compiler has already inferred that T is a mutable reference, so y is implicitly reborrowed. (This example is a good illustration why adding compiler magic to make things "just work" generally is a bad idea. Explicit is better than implicit.)

不幸的是,目前在 Rust 参考中没有记录这种行为.

另见:

这篇关于可变引用有移动语义吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆