【问题标题】:What do I have to do to solve a "use of moved value" error?我该怎么做才能解决“使用移动值”错误?
【发布时间】:2015-03-01 22:43:48
【问题描述】:

我正在尝试计算 Rust 中的第 10,001 个素数(Project Euler 7),作为其中的一部分,我检查整数是否为素数的方法引用了一个向量:

fn main() {
    let mut count: u32 = 1;
    let mut num: u64 = 1;
    let mut primes: Vec<u64> = Vec::new();
    primes.push(2);

    while count < 10001 {
        num += 2;
        if vectorIsPrime(num, primes) {
            count += 1;
            primes.push(num);
        }
    }
}

fn vectorIsPrime(num: u64, p: Vec<u64>) -> bool {
    for i in p {
        if num > i && num % i != 0 {
            return false;
        }
    }

    true
}

当我尝试引用向量时,出现以下错误:

error[E0382]: use of moved value: `primes`
 --> src/main.rs:9:31
  |
9 |         if vectorIsPrime(num, primes) {
  |                               ^^^^^^ value moved here, in previous iteration of loop
  |
  = note: move occurs because `primes` has type `std::vec::Vec<u64>`, which does not implement the `Copy` trait

我必须对primes 做什么才能在vectorIsPrime 函数中访问它?

【问题讨论】:

  • 与问题正交:1) 分享 rust-playground 链接总是有用的。 2)逻辑不正确(count +=1)应该发生在外面。

标签: rust


【解决方案1】:

使用您的函数vectorIsPrime() 的当前定义,该函数指定它需要参数的所有权,因为您按值传递它

当函数需要按值传递参数时,编译器将通过检查其是否实现 trait Copy 来检查该值是否可以被复制。

  • 如果是,则复制该值(使用 memcpy)并将其提供给函数,您仍然可以继续使用原始值。
  • 如果没有,则将该值移至给定函数,之后调用者将无法使用它

这就是你的错误信息的意思。

但是,大多数函数不需要参数的所有权:它们可以在“借用引用”上工作,这意味着它们实际上并不拥有该值(例如,不能将其放入容器或销毁它)。

fn main() {
    let mut count: u32 = 1;
    let mut num: u64 = 1;
    let mut primes: Vec<u64> = Vec::new();
    primes.push(2);

    while count < 10001 {
        num += 2;
        if vector_is_prime(num, &primes) {
            count += 1;
            primes.push(num);
        }
    }
}

fn vector_is_prime(num: u64, p: &[u64]) -> bool {
    for &i in p {
        if num > i && num % i != 0 {
            return false;
        }
    }
    true
}

函数vector_is_prime() 现在指定它只需要一个切片,即一个指向数组的借用指针(包括它的大小),您可以使用借用运算符&amp; 从向量中获取该数组.

有关所有权的更多信息,我邀请您阅读本书中与ownership 相关的部分。

【讨论】:

  • 为什么i in for &amp;i in p{ 前面需要&amp;?没有它,代码会引发编译错误,我很好奇为什么。
  • @mjkaufer,这意味着i 是一个引用,因此,在循环中,i 将是值,而不是引用。如果你忽略它,在进行比较时你需要 *i's。
  • vector_is_prime中的prime测试条件不正确,应该是:if num &gt; i &amp;&amp; num % i == 0
【解决方案2】:

正如我所说,Rust 是一种“以价值为导向”的语言。这意味着如果你像这样定义素数

let primes: Vec<u64> = …

它不是对向量的引用。它实际上是一个存储Vec&lt;u64&gt; 类型的 的变量,就像任何u64 变量存储一个u64 值一样。这意味着如果你将它传递给这样定义的函数

fn vec_is_prime(num: u64, vec: Vec<u64>) -> bool { … }

该函数将获得自己的u64 值和自己的Vec&lt;u64&gt; 值。

u64Vec&lt;u64&gt; 之间的区别在于u64 值可以轻松复制到另一个位置,而Vec&lt;u64&gt; 值只能移动 到另一个位置。如果你想给vec_is_prime 函数赋予它自己的Vec&lt;u64&gt; 值,同时在main 中为自己保留一个值,你必须以某种方式复制它。这就是clone() 的用途。你必须在这里明确的原因是因为这个操作并不便宜。这是 Rust 的一件好事:发现昂贵的操作并不难。所以,你可以像这样调用函数

if vec_is_prime(num, primes.clone()) { …

但这并不是你真正想要的,实际上。该函数不需要自己的Vec&lt;64&gt; 值。它只需要借它一小会儿。在这种情况下,借用更加有效和适用:

fn vec_is_prime(num: u64, vec: &Vec<u64>) -> bool { …

现在调用它需要“借用运算符”:

if vec_is_prime(num, &primes) { …

好多了。但我们仍然可以改进它。如果函数想借用Vec&lt;T&gt; 只是为了阅读,最好改用&amp;[T]

fn vec_is_prime(num: u64, vec: &[u64]) -> bool { …

只是更笼统。现在,您可以将 Vec 的特定部分借给函数或完全其他的东西(不一定是Vec,只要这个东西将其值连续存储在内存中,就像静态查找表一样)。还不错的是,由于强制规则,您不需要在调用站点更改任何内容。您仍然可以使用 &amp;primes 作为参数调用此函数。

对于String&amp;str,情况是一样的。 String 用于存储字符串值,这种类型的变量拥有该值。 &amp;str是借用的。

【讨论】:

  • 一个小细节:在迭代时,似乎正如@Vaelden 所写,迭代变量i 需要写为&amp;i
  • @mjkaufer:这是一种可能性,是的。另一种方法是取消引用 i 在您使用它的位置。
  • 对借来的切片进行迭代会得到借来的元素。以不同的方式进行操作需要元素类型为CopyClone。如果在 for 循环中使用 &amp;ii 将是向量元素的副本。
【解决方案3】:

您将primes 的值移动到函数vectorIsPrime(顺便说一句,Rust 按照惯例使用snake_case)。您还有其他选择,但最好的选择是借用矢量而不是移动它:

fn vector_is_prime(num: u64, p: &Vec<u64>) -> bool { … }

然后传递对它的引用:

vector_is_prime(num, &primes)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多