5

I'm quite new to Rust and still having issues with the concept of lifetime specifiers.

I have a Vec (or any other collection) of references. Now I want to replace a specific reference with another reference to a newly created local variable. The problem now is, that the local new variable has a shorter lifetime than the vector which would invalidate the reference.

An example code that tries do do what I want but throws an compile time error:

fn main() {
    let values = ["one".to_string(), "lorem".to_string(), "three".to_string()];
    let borrowed_values = vec![&values[0], &values[1], &values[2]];
    println!("{:?}", do_the_impossible(borrowed_values));
}

fn do_the_impossible(mut data: Vec<&String>) -> Vec<&String> {
    let replacement = "two".to_string();
    data[1] = &replacement;
    return data;
}

The question now is: How do I create a new variable with the same lifetime as the vector?

I want to use references because cloning the objects would be pretty expensive.

My initial idea was to use lifetime specifiers which indicate that the local variable should live as long as the vector does. But I couldn't find a syntactical right way to do this. Is it even possible to specify the lifetime of a local variable?

And if not, is there any other way to avoid cloning the elements of the vector? My last resort would be using an Rc pointer.

Thanks in advance!

Acorn
  • 24,970
  • 5
  • 40
  • 69
Pilikio
  • 302
  • 4
  • 13
  • 1
    Does this answer your question? [Is there any way to return a reference to a variable created in a function?](https://stackoverflow.com/questions/32682876/is-there-any-way-to-return-a-reference-to-a-variable-created-in-a-function) tl;dr: no, you can't do that, in Rust or in any language. Return an owned value instead. – trent Sep 12 '20 at 21:29
  • 2
    *I want to use references because cloning the objects would be pretty expensive.* -- This is a fairly common mistake for people new to Rust. Because you return something owned doesn't mean it will be cloned. For instance, `String` is a type that owns a `str`, but even if you have a `String` a megabyte long, returning it from a function only copies 24 bytes at most (same for any `Vec`). – trent Sep 12 '20 at 21:35
  • Maybe we're misunderstanding each other, the function returns an owned Vec. But the original Vec doesn't own its values. I want to return a valid Vec where only one of the values is changed, the original Vec stays the same, and all that with as less cloning as possible. – Pilikio Sep 13 '20 at 14:46

1 Answers1

5

This sounds like a use case for std::borrow::Cow, which allows you to have a value which may be backed by a borrow or an owned value.

You would instead write something like:

use std::borrow::Cow;

fn main() {
    let values = ["one".to_string(), "lorem".to_string(), "three".to_string()];
    let borrowed_values: Vec<_> = values.iter().map(Cow::Borrowed).collect();
    println!("{:?}", do_the_impossible(borrowed_values));
}

fn do_the_impossible<'a>(mut data: Vec<Cow<'a, String>>) -> Vec<Cow<'a, String>> {
    let replacement = "two".to_string();
    data[1] = Cow::Owned(replacement);
    return data;
}
Daniel Wagner-Hall
  • 2,446
  • 1
  • 20
  • 18
  • Just to check whether I'm not misunderstanding something: If I call borrowed_values.clone() after the do_the_impossible() call. Are the values (Strings) being copied or just the references (Cow)? And what if one of the Cow elements is a Cow::Owned? Will the value be cloned or borrowed? – Pilikio Sep 13 '20 at 14:27
  • We can find this out by looking at the implementation of `Clone` for `Cow`: https://doc.rust-lang.org/src/alloc/borrow.rs.html#185-202 - if the underlying value was a Borrow, it will be another borrow, if the underlying value was an Owned, it will be a clone of it. You could also implement your own version of `Cow` if you wanted different semantics - it's just an `enum` which implements some useful traits and functions :) – Daniel Wagner-Hall Sep 13 '20 at 19:49
  • Ok thanks, it looks like this is very close to what i wanted and can be tweak to exactly solve my problem. – Pilikio Sep 14 '20 at 04:55