【译】理解Rust中的局部移动

原文标题:Understanding Partial Moves in Rust
原文链接:https://whileydave.com/2020/11/30/understanding-partial-moves-in-rust/
公众号: Rust 碎碎念
翻译 by: Praying

最近,我一直在研究Rust,虽然从很多方面来看它都是一门十分优秀的语言,但我也发现了很多不易察觉的复杂性。其中一个例子就是,不太引人注意的局部移动(partial move) 。因此,我在想,为什么不写一篇文章来介绍它呢?

所有权(简洁版)

我不准备在这里介绍Rust中所有权和借用的全部细节。不过,这里我们仍然需要一些背景知识使得局部移动(partial move)能够讲得通。下面通过是一个box和一个箭头头来表示Rust中的所有权!

在一个没有所有权的世界里(想想Java、C/C++),我们可以通过别名(aliasing)毫无限制地引用数据。我们可以让两个引用指向相同的数据,数据之间互相引用等等。

在所有权的世界里(即Rust),一个引用可以拥有它所指向的数据。在这种情况下,不允许再有对该数据的持有所有权的其他引用(owning references)。例如,如果两个引用指向相同的数据(即上图中中间的情况),只有一个引用可以是所有者。

这种对关联所有权的引用的限制影响了我们写程序的方式。假定我们试图从变量p拷贝一份持有所有权的引用到变量q:

这就行不通了,因为它破坏了所有权的不变性 。如果允许这种情况,我们将会对同一份数据持有两个带有所有权的引用,而这是不被允许的。所以,我们该怎么办呢?我们可以移动(move)它

在上图中,变量p的值经过移动(move)操作后已经作废,在对p赋予新的值之前,我们不能再使用它了。当然,这也对我们使用Rust编写程序产生了相当大的影响。但是,我不准备在这里讨论这个问题。

局部移动(Partial Moves)

目前为止,我已经看到了一次性移动(move)整个变量(例如,上面中从p移动到q)。此外,我们还可以执行一个局部移动(partial move),在局部移动(partial move)中,可以仅移动(move)给定变量的一部分内容。假定现在我们的变量p是一个pair,其中每个元素包含一个持有所有权的引用。然后,我们可以把p中的第二个元素移动到另一个变量q中,如下图所示:

这个例子的有趣之处在于,不同于之前的情况,尽管变量p的一部分内容已经失效,但它仍然可以以一种受限的方式被使用。具体来讲,我们可以使用p.0但是不能使用p.1。此外,Rust阻止我们对变量p进行整个的拷贝(copy)或移动(move)(尽管在我看来,这并没有必要)。上面的示例转为代码,如下:

fn main() { 
    let mut x = 123;
    let mut y = 456;
    let mut p = (&mut x,&mut y);
    let mut q = p.1;
    ...
}

目前为止,一切都好。但是,当对...进行替换, 比如替换为let mut z = p;时,我们会得到下面的错误信息:

error[E0382]: use of partially moved value: `p`
 --> src/main.rs:6:17
  |
5 | let mut q = p.1;
  |             --- value partially moved here
6 | let mut z = p;
  |             ^ value used here after partial move

只是简单地告诉我们,我们不能使用一个由于之前的移动(move)操作而已经失效的值。个人而言,我不太理解为什么Rust阻止这种移动(move)操作,因为能够很容易地推导出z只是被部分定义(译注:即另一部分无效),就像它对p所做的那样。想必是,尽管可以推导,但是通过某个引用对p赋值必然会导致某些问题吧。

总结

Rust是一门相当酷的语言,但是仍然有很多微妙的特性。希望这篇文章可以帮助解答其中的一个困惑。

posted @ 2020-12-02 11:39  Praying  阅读(827)  评论(0编辑  收藏  举报