4

I am trying to reduce a reference of a Vec to its sum so I can calculate its mean. I am running into complier issues though and I am not following how things are not being borrowed/referenced correctly.

// 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,
    }
}

I'm receiving a complier error when I run cargo run and rust-analyzer also highlights the &(a + b) of the reduce method as wrong too. The text of the error is below but I've also attached the image to clearly show what it is referencing too.

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

Error saying temporary value created here but returns a reference to data owner by the current function

I am unsure what is wrong here as I understand .iter() returns an Iter reference to the Vec so shouldn't its reduced values of a and b already be &i32? When I remove & from &(a + b), I get the following complier error "expected &i32, found i32 help: consider borrowing here: &(a + b)".

Note I am just learning Rust and I am less than halfway through its tutorial so please feel free to explain the solution as if I'm a newbie (since I am).

I am using rust version 1.52.1 with rustup version 1.24.1 on windows 10 in VSCode.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
alferguson_JS
  • 55
  • 1
  • 6
  • Why not use `iter().sum()`? – Ivan C Jun 07 '21 at 05:43
  • 2
    Please paste the error message as text rather than as screenshot. Images are not searchable and are much harder to edit. – user4815162342 Jun 07 '21 at 05:45
  • @user4815162342 I did paste the error as text... – alferguson_JS Jun 07 '21 at 05:48
  • `&(a + b)` doesn't make sense, except perhaps for passing downward, say to a function that expects a reference. A reference has to be a reference to _something_, and you're returning a reference to a temporary value, which is like a local variable, destroyed after the closure is done. Use `integers.iter().copied()` to get an iterator over numbers rather than references, and your drop the `&` in both places. – user4815162342 Jun 07 '21 at 05:49
  • @user4815162342 I think I understand now. The closure created in the reduce method, once closed, frees up the memory for the values created by a + b which causes the complier to error since the reference I had to it would be invalid. I added the copied method to it and it worked. Thank you. If you paste your comment as an answer, I will mark it as the answer. – alferguson_JS Jun 07 '21 at 05:54
  • @alferguson_JS I've now posted an answer. Regarding error-as-text, what I meant is that you can replace the screenshot with the pasted text of the full error, enclosed in triple backticks. The screenshot is not only unsearchable and uneditable, but hard to read for people with different font sizes. – user4815162342 Jun 07 '21 at 06:36
  • 3
    Side note: [Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?](https://stackoverflow.com/questions/40006219/why-is-it-discouraged-to-accept-a-reference-to-a-string-string-vec-vec-o?rq=1) – Jmb Jun 07 '21 at 06:51
  • @user4815162342 I didn't realize it would keep its formatting like that, I've updated my question to include the error-as-text, thank you. – alferguson_JS Jun 08 '21 at 01:06

2 Answers2

12

Returning &(a + b) doesn't work because it attempts to return a reference to a temporary value. The result of a + b behaves like an unnamed local variable, i.e. it's local to the closure and destroyed prior to its return. The most elegant way to fix the problem is to use integers.iter().copied() to obtain an iterator over actual numbers rather than references. This allows omitting & in both places.

Note that an expression like &(a + b) is not always meaningless. It's quite useful when you pass the reference downward, say to a function or an operator that expects a reference. In that case e.g. f(&(a + b)) is shorthand for { let _tmp = a + b; f(&tmp) }, and makes perfect sense.

Unrelated to the above, you probably want your function to accept a slice, &[i32], rather than reference to a vector. This will work with vectors unchanged and will make your function accept other contiguous slices of memory, e.g. coming from arrays. (See here for details.)

user4815162342
  • 141,790
  • 18
  • 296
  • 355
4

Your issue here is that function passed to reduce must return a value of the same type as original iterator's item, which for iterators created through Iter trait is always &T. However, you can't return a reference from a function, because it would point to a freed stack frame.
Your options are:

  • to use into_iter() instead, which consumes the collection it's called on and yields owned values,
  • to use iter().cloned() which will clone the values, again yielding an iterator over owned values, although it might be costly for non-primitive types.

But in this specific case of summing up an iterator, you should just use iter().sum().

Ivan C
  • 1,772
  • 1
  • 8
  • 13