类型推断、借用和所有权转移

8

我正在学习Rust,遇到了一些令人困惑的问题。以下代码可以编译并且按预期工作(编辑:添加了除测试函数外的代码,此前被省略):

struct Container<'a> {
    contents : &'a mut i32,
}

fn main() {
    let mut one = Container { contents: &mut 5 };
    test(&mut one);
    println!("Contents: {}",one.contents);
}

fn test<'a>(mut x : &'a mut Container) {
    *x.contents += 1;
    let y = x;
    *y.contents += 1;
    x = y;
    println!("{:?}",*x.contents)
}

现在在这个语句中。
let y = x;

类型是隐式推断的。因为x的类型是&'a mut Container,我认为以下代码等效:

let y: &'a mut Container = x;

但是当我这样做时,编译器会出问题:

test_3.rs:25:5: 25:10 error: cannot assign to `x` because it is borrowed
test_3.rs:25     x = y;
                 ^~~~~
test_3.rs:23:33: 23:34 note: borrow of `x` occurs here
test_3.rs:23     let y: &'a mut Container = x;

在正确的示例中,为什么此时 x 没有被借用? 我测试了一下,在正确的示例中删除了 x = y; 这行代码后,编译器报错:

test_3.rs:24:13: 24:14 note: `x` moved here because it has type `&mut Container<'_>`, which is moved by default

当我不明确定义类型时,我的代码出现了移动操作,而在其他情况下则是借用操作。发生了什么?如何在明确给定类型的同时获得与以前相同的行为?是什么原因导致了一种情况下的移动操作,而另一种情况下则是借用操作呢?

带有完整程序的编辑


2
也许有一个像这样的示例 - Shepmaster
抱歉,这是完整的代码链接:http://is.gd/9oz2kT。你的示例完美地捕捉了问题。 - Mark Mywords
1
相关链接:https://dev59.com/R1wY5IYBdhLWcg3w2Kwv - Veedrac
1个回答

8

当你进行操作时

let y = x;

发生了一次移动。可以这样说,x 被清空,所有权被转移到 y

当您执行以下任何操作之一时:

let y: &mut _ = x;
let y: &'a mut _ = x;

为了匹配生命周期,x 被重新借用。这大概可以翻译为:

let y: &mut _ = &mut *x;
let y: &'a mut _ = &mut *x;

这将使得x非空,持有一个别名可变借用。因此,对它的赋值必须等待y被销毁。或者,您可以预先移动它。
let tmp = x;
let y: &'a mut _ = tmp;

我承认这是一个不太明显的行为,很遗憾你不能在借用整个值的情况下借用值的内容。


1
“持有一个别名可变借用” - 我的印象是,这明显是安全 Rust 禁止的事情? - Shepmaster
3
Rust并不禁止别名可变借用,它禁止同时使用多个别名可变借用或以非词法方式重叠它们。当你写&mut x时,x仍然是一个有效的堆栈位置—只是不合法读取或写入它。 - Veedrac
有趣。我一直想知道编译器用来确定移动与重新借用的确切规则。到目前为止,我认为重新借用只会在函数调用中发生,其中函数需要一个 &mut。但显然,在函数内部更明确地表达也可以符合重新借用的条件。 - sellibitze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接