Returns a reference to data owned by the current function error

I have function like this. I want to change function f's argument &'a [u8] to &'a Vec<u8> and its return type Vec<&'a [u8]> to Vec<&'a Vect<u8>>.

After changing the code, Rust cargo build complains the error returns a reference to data owned by the current function. I know like 1. 2 I have to store the variable somewhere outside my closure so that the life time can stay longer if I understand that error correctly. However, I do not know how to change that.

My attempting:

  • Add .as_ref before the windows(2), but there is no as_ref() function.
  • Create another myf fn fn myf<'a>(piece: &'a Vec<u8>, part: Vec<&'a (usize, u32)>) -> &'a Vec<u8> { &piece[part[0].0..part[1].0].to_vec() }, and replace .map(|part| &piece[part[0].0..part[1].0].to_vec()) with .map(|part| myf). An error value of type Vec<&Vec<u8>> cannot be built from std::iter::Iterator<Item=for<'a> fn(&'a Vec<u8>, Vec<&'a (usize, u32)>) -> &'a Vec<u8> {myf}> is thrown.

How to correct this error? Thanks

/* With this function the program can get compiled, and is executed successfully.
fn f<'a>(piece: &'a [u8]) -> Vec<&'a [u8]> {
  [(0, 4294967295u32), (2, 4294967295u32), (4, 4294967295u32)]
  .windows(2)
  .map(|part| &piece[part[0].0..part[1].0])
  .collect()
}
*/

// this one throws returns a reference to data owned by the current function error
fn f<'a>(piece: &'a Vec<u8>) -> Vec<&'a Vec<u8>> {
  [(0, 4294967295u32), (2, 4294967295u32), (4, 4294967295u32)]
  .windows(2)
  .map(|part| &piece[part[0].0..part[1].0].to_vec())
  .collect()
}

fn main() {
  let vec = &Vec::from(b"abcd");
  let result = f(vec);
  println!("result: {:?}", result);
}

When you call methods like to_vec or collect you create temporary data structures that are owned by that function. Try this instead:

fn f<'a>(piece: &'a Vec<u8>) -> impl Iterator<Item= &'a[u8]> {
  [(0, 4294967295u32), (2, 4294967295u32), (4, 4294967295u32)]
  .windows(2)
  .map(|part| &piece[part[0].0..part[1].0])
}

fn main() {
  let vec = &Vec::from(b"abcd");
  let result = f(vec).collect::<Vec<_>>();
  println!("result: {:?}", result);
}
1 Like

So it's because the data created in the closure is short live, which is destroyed after .map() function. After that the code continues to call collect(), which requires the use the data created inside the closure i.e. &piece[part[0].0..part[1].0]. Therefore, rather than calling collect() after map(), we let the code accomplishes its function first - returning an impl Iterator<Item= &'a[u8]>. Then, followed by the function f, calling the collect() because in the main function after f(vec), the temporary data created now is still alive, which then would be safe to execute the collect::<Vect<_>>(). Am I correct?

Also, how can I know what kind of function, other than to_vec(), collect(), that may create a temporary data structure owned by the function? It seems to me like it's a bit like some functions can be classified to action, while others are just expression in FP.

Thank you for the advice.

Yes, exactly.

Just a minor clarification here. f is returning the Vec, we could actually get away with this:

fn f<'a>(piece: &'a Vec<u8>) -> Vec<&'a[u8]> {
  [(0, 4294967295u32), (2, 4294967295u32), (4, 4294967295u32)]
  .windows(2)
  .map(|part| &piece[part[0].0..part[1].0])
  .collect()
}

fn main() {
  let vec = &Vec::from(b"abcd");
  let result = f(vec);
  println!("result: {:?}", result);
}

But the whole gist about not returning temporary values still holds. It's just that in this case the Vec doesn't hold a temporary value anymore.

It would be a case-by-case thing. You need to check the documentation for each method that you call, but to be honest methods that perform transformations usually have names that hint you what will happen, like (in)to_something, collect, from_something_else, etc.

1 Like

you can omit those lifetimes too, the compiler can infer them based on lifetime elision rules

1 Like

Yes, of course. I just tried to do the minimum required changes to achieve what the OP wanted. Sometimes introducing more changes is counterproductive because the OP might not be able to pinpoint what the required change was.

1 Like

yes after i commented i noticed that he explicitly asked for those lifetimes but just in case he doesn't know

2 Likes

Thanks that's clear.

I am still not familiar with Rust lifetime annotation. I need more practice. Thanks for the comment, that's useful!

1 Like