xx_VxR_xx
xx_VxR_xx

Reputation: 33

Rust error :Cannot return value referencing temporary value

I'm trying to make a code that returns the mode of a list of given numbers. Here's the code :

 use std::collections::HashMap;

 fn mode (vector: &Vec<i32>) -> Vec<&&i32> {
    let mut occurrences = HashMap::new();
    let mut n= Vec::new(); 
    let mut mode = Vec::new(); 

    for i in vector {
    let j= occurrences.entry(i).or_insert(0); 
        *j+=1;
    }

    for (num, occ) in occurrences.clone().iter() {

        if occ> n[0] {
            n.clear();
            mode.clear();

            n.push(occ);
            mode.push(num);
        } else if occ== n[0] {
                mode.push(num);             
            }
        }

    mode
}

fn main () {
    let mut numbers: Vec<i32>= vec![1,5,2,2,5,3];    // 2 and 5 are the mode
    numbers.sort();
    

    println!("the mode is {:?}:", mode(&numbers));
}

I used a vector for the mode since a dataset could be multimodal. Anyway, I'm getting the following error:

error[E0515]: cannot return value referencing temporary value
 --> src/main.rs:26:5
  |
13 |     for (num, occ) in occurrences.clone().iter() {
  |                       ------------------- temporary value created here
...
26 |     mode
  |     ^^^^ returns a value referencing data owned by the current function

Upvotes: 3

Views: 6615

Answers (1)

cameron1024
cameron1024

Reputation: 10136

When you return from the current function, any owned values are destroyed (other than the ones being returned from the function), and any data referencing that destroyed data therefore cannot be returned, e.g.:

fn example() -> &str {
  let s = String::from("hello");  // owned data
  &s  // error: returns a value referencing data owned by the current function

  // you can imagine this is added by the compiler
  drop(s);
} 

The issue you have comes from iter(). iter() returns an iterator of shared references:

let values: Vec<i32> = vec![1, 2, 3];
for i in values.iter() {
  // i is a &i32
}

for i in values {
  // i is an i32
}

So when you call occurrences.clone().iter() you're creating a temporary value (via clone()) which is owned by the current function, then iterating over that data via shared reference. When you destructure the tuple in (num, occ), these are also shared references.

Because you later call mode.push(num), Rust realizes that mode has the type Vec<&i32>. However, there is an implicit lifetime here. The lifetime of num is essentially the lifetime of the current function (let's call that 'a), so the full type of mode is Vec<&'a i32>.

Because of that, you can't return it from the current function.

To fix

Removing iter() should work, since then you will be iterating over owned values. You might also find that you can remove .clone() too, I haven't looked too closely but it seems like it's redundant.

A couple of other points while you're here:

  • It's rare to interact with &Vec<Foo>, instead it's much more usual to use slices: &[Foo]. They're more general, and in almost all cases more performant (you can still pass your data in like: &numbers)
  • Check out clippy, it has a bunch of linter rules that can catch a bunch of errors much earlier, and usually does a good job explaining them: https://github.com/rust-lang/rust-clippy

Upvotes: 3

Related Questions