Rust中关于Vec引用的Reduce方法

4

我试图将 Vec 的引用缩减为其总和,以便计算其平均值。但是,我遇到了编译器问题,我不知道哪里出现了借用/引用错误。

// Given a list of integers, use a vector and return the mean (the average value), median (when sorted, the value in the middle position), and mode (the value that occurs most often; a hash map will be helpful here) of the list.
fn main() {
    let list_of_integers = vec![200, -6_000, 3, 0, 23, 99, -1];

    let mean_ans = mean(&list_of_integers);
    // Other code that will also use list_of_integers hence why I want to reference list_of_integers so it doesn't get removed from memory

    println!("mean is {}", mean_ans);
}

fn mean(integers: &Vec<i32>) -> i32 {
    let length = integers.len() as i32;
    let sum = integers.iter().reduce(|&a, &b| &(a + b));

    match sum {
        Some(v) => v / length,
        None => 0,
    }
}

当我运行cargo run时,我收到了一个编译器错误,rust-analyzer也将reduce方法中的&(a + b)标记为错误。下面是错误的文本,但我也附上了图片以清楚地显示它所引用的内容。

error[E0515]: cannot return reference to temporary value
  --> src\main.rs:13:47
   |
13 |     let sum = integers.iter().reduce(|&a, &b| &(a + b));
   |                                               ^-------
   |                                               ||
   |                                               |temporary value created here
   |                                               returns a reference to data owned by the current function

error: aborting due to previous error

错误信息:临时值在此创建,但返回对当前函数拥有者数据的引用

我不确定这里出了什么问题,因为我了解.iter()返回一个Vec的Iter引用,所以它的值已经被减小为&i32的a和b吗?当我从&(a + b)中删除&时,会得到以下编译器错误:“expected &i32, found i32 help: consider borrowing here: &(a + b)”。

请注意,我正在学习Rust,而且还没有完成其教程的一半,所以请随意将解决方案解释得像我是新手一样(因为我确实是新手)。

我使用的是Windows 10中的VSCode上的rust版本1.52.1和rustup版本1.24.1。


2
请将错误消息粘贴为文本,而不是作为屏幕截图。图像无法搜索,并且更难编辑。 - user4815162342
除非是向期望引用的函数传递,否则&(a + b) 毫无意义。 引用必须是对“某物”的引用,你正在返回对临时值的引用,这就像是一个局部变量,在闭包完成后被销毁。 请使用 integers.iter().copied() 来获取数字上的迭代器而不是引用,并在两个地方删除& - user4815162342
@user4815162342 我现在明白了。在 reduce 方法中创建的闭包一旦关闭,就会释放由 a + b 创建的值的内存,这会导致编译器出错,因为我对它的引用将无效。我添加了复制的方法,它可以正常工作。谢谢。如果您将您的评论粘贴为答案,我将把它标记为答案。 - alferguson_JS
3
附注:为什么不鼓励将String (&String)、Vec (&Vec)或Box (&Box)的引用作为函数参数?这是因为这些类型拥有所有权,如果将它们作为引用传递给函数,则可能会导致所有权问题和潜在的内存安全问题。相反,建议使用借用(&)或所有权转移(move)来传递这些类型的数据。 - Jmb
@user4815162342 我没想到它会保留那样的格式,我已经更新了我的问题,包括错误文本,谢谢。 - alferguson_JS
显示剩余3条评论
2个回答

13

返回&(a + b)是不起作用的,因为它试图返回对临时值的引用。 a + b的结果就像一个无名的局部变量,即它是闭包内部的局部变量,在返回之前被销毁。解决这个问题最优雅的方法是使用integers.iter().copied()来获取一个迭代器,该迭代器包含实际的数字而不是引用。这样可以在两个地方都省略&

请注意,像&(a + b)这样的表达式并不总是没有意义的。当您将引用向下传递时,例如传递给期望引用的函数或运算符时,它非常有用。在这种情况下,f(&(a + b))相当于{ let _tmp = a + b; f(&tmp) },并且完全合理。

与上述无关的是,您可能希望您的函数接受一个切片&[i32],而不是对向量的引用。这将使函数能够与向量保持不变,并接受其他连续的内存切片,例如来自数组的切片。(有关详细信息,请参见此处。)


4
您的问题在于传递给 reduce 函数的函数必须返回与原始迭代器项相同类型的值,而通过 Iter 特性创建的迭代器始终为 &T。但是,您不能从函数返回引用,因为它将指向已释放的堆栈帧。
您的选项是:
  • 使用 into_iter() 代替,它会消耗调用它的集合并产生拥有的值。
  • 使用 iter().cloned(),它将克隆值,再次产生拥有值的迭代器,尽管对于非基元类型可能会很昂贵。
但是,在这种特定情况下,对于迭代器求和,您应该只使用 iter().sum()。

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