【问题标题】:Why is the mutable reference not moved here?为什么可变引用没有移到这里?
【发布时间】:2015-11-16 05:29:42
【问题描述】:

我的印象是可变引用(即&mut T)总是被移动。这是完全合理的,因为它们允许独占的可变访问。 在下面的代码中,我将一个可变引用分配给另一个可变引用,并移动了原始引用。结果我不能再使用原来的了:

let mut value = 900;
let r_original = &mut value;
let r_new = r_original;
*r_original; // error: use of moved value *r_original

如果我有这样的功能:

fn make_move(_: &mut i32) {
}

并将我原来的示例修改为如下所示:

let mut value = 900;
let r_original = &mut value;
make_move(r_original);
*r_original; // no complain

我希望当我用它调用函数make_move 时,可变引用r_original 会被移动。然而,这不会发生。通话后我仍然可以使用参考。

如果我使用泛型函数make_move_gen

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

然后这样称呼它:

let mut value = 900;
let r_original = &mut value;
make_move_gen(r_original);
*r_original; // error: use of moved value *r_original

引用再次移动,因此程序的行为符合我的预期。 为什么调用make_move函数时引用没有移动?

Code example

【问题讨论】:

  • 显式实例化 (make_move::&lt;&amp;mut i32&gt;(r_original);) 像原始函数一样工作(没有移动)。迷人;我假设借用检查发生在类型推断之前。
  • 从 dacker 的回答中,我假设是这种情况:明确地将类型注释为可变引用会触发内容的重新借用而不是移动,一旦新参考(在 make_move 的范围内)超出范围。

标签: rust move-semantics


【解决方案1】:

这实际上可能有一个很好的理由。

&amp;mut T 实际上不是一种类型:所有借用都由一些(可能无法表达的)生命周期参数化。

当一个人写作时

fn move_try(val: &mut ()) {
    { let new = val; }
    *val
}

fn main() {
    move_try(&mut ());
}

类型推断引擎推断typeof new == typeof val,因此它们共享原始生命周期。这意味着从new 借用不会结束,直到从val 借用才结束。

这意味着它相当于

fn move_try<'a>(val: &'a mut ()) {
    { let new: &'a mut _ = val; }
    *val
}

fn main() {
    move_try(&mut ());
}

但是,当你写的时候

fn move_try(val: &mut ()) {
    { let new: &mut _ = val; }
    *val
}

fn main() {
    move_try(&mut ());
}

发生了强制转换——同样的事情可以让你抛弃指针的可变性。这意味着生命周期是一些(似乎无法指定)'b &lt; 'a。这涉及强制转换,因此涉及重借,因此重借可能会超出范围。

总是重新借用规则可能会更好,但显式声明并没有太大问题。

【讨论】:

  • 我从来没有这样想过。然而,这似乎是合乎逻辑的。 “抛弃指针可变性”是指从可变引用中获取共享引用?就像 let r: &i32 = r_mut;其中 r_mut 是 &mut i32。
  • 很好的解释。这真的很有意义。我对'b 的猜测是:它是'a 的交集,它包含move_try 的主体和定义new 的嵌套范围;所以'b 将是这个嵌套范围,new 的借用结束。
  • 根据解释,前 2 个 sn-ps 似乎等效,但它们显示不同的错误消息 Playground。第一个 sn-p 表示价值已移动,而第二个 sn-p 表示仍是借来的。我是不是误会了什么?
  • @Mihir 从技术上讲,let new: &amp;'a mut _ = val; 是一个重借,它只是一个无操作重借,因为生命周期是相同的。
  • @Mihir 我不认为你可以,除非你有一个类型变量中的整个类型。
【解决方案2】:

我问了一些类似here的问题。

似乎在某些(很多?)情况下,不是移动,而是重新借入。没有违反内存安全性,只有“移动”的值仍然存在。我也找不到关于这种行为的任何文档。

@Levans 打开了一个 github 问题 here,尽管我并不完全相信这只是一个文档问题:可靠地移出 &mut 引用似乎是 Rust 所有权方法的核心。

【讨论】:

    【解决方案3】:

    它是implicit reborrow。不是well documented的话题。

    这个问题已经很好回答了:

    1. how implicit reborrow works
    2. how reborrow works along with borrow split

    【讨论】:

      【解决方案4】:

      如果我稍微调整一下通用的,它也不会抱怨

      fn make_move_gen<T>(_: &mut T) {
      }
      

      let _ = *r_original; 
      

      【讨论】:

      • 这并不能真正帮助回答这种行为背后的“为什么”。第二个例子只是取消引用,在这里并不真正相关。
      • @kmdreko 通过第二个例子,我的意思是如果我改变了“*r_original;”在问题的代码块中“let _ = *r_original;”,它会编译。我在玩,在我看来,当我执行“let _ = r_original;”时,它不会执行 de-ref,因此即使值确实已被移动,它也不会抱怨。
      • 哦,这很有趣!它确实remove the error。抱歉造成误会,我已更改投票。
      • 感谢您的评论,我长期以来一直是一个纯粹的消费者,并且正在学习贡献一点回报:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-15
      • 2018-01-05
      • 2018-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多