Return what is allocated on the stack

I have the following simplified code where struct A contains a specific attribute. I would like to create new instances of A from an existing version of this attribute, but how do I make the lifetime of a new attribute value last after a function call?

 pub struct A<'a> { some_attr: &'a str, } impl<'a> A<'a> { fn combine(orig: &'a str) -> A<'a> { let attr = &*(orig.to_string() + "suffix"); A { some_attr: attr } } } fn main() { println!("{}", A::combine("blah").some_attr); } 

In the above code

 error[E0597]: borrowed value does not live long enough --> src/main.rs:7:22 | 7 | let attr = &*(orig.to_string() + "suffix"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough 8 | A { some_attr: attr } 9 | } | - temporary value only lives until here | note: borrowed value must be valid for the lifetime 'a as defined on the impl at 5:1... --> src/main.rs:5:1 | 5 | / impl<'a> A<'a> { 6 | | fn combine(orig: &'a str) -> A<'a> { 7 | | let attr = &*(orig.to_string() + "suffix"); 8 | | A { some_attr: attr } 9 | | } 10| | } | |_^ 
+6
source share
1 answer

This question, of course, was answered earlier, but I do not close it as a duplicate, because here the code is slightly different, and I think it is important.

Notice how you defined your function:

 fn combine(orig: &'a str) -> A<'a> 

It says that it will return a value of type A whose internals live exactly as long as the provided string. However, the function body violates this expression:

 let attr = &*(orig.to_string() + "suffix"); A { some_attr: attr } 

Here you create a new String obtained from orig , take a piece of it and try to return it inside A However, the lifetime of the implicit variable created for orig.to_string() + "suffix" is strictly less than the lifetime of the input parameter. Therefore, your program is rejected.

Another, more practical way to look at this is to assume that the string created by to_string() and concatenation should live somewhere. However, you only return the borrowed piece. Thus, when the function exits, the string is destroyed, and the returned fragment becomes invalid. This is exactly the situation that Rust is preventing.

To overcome this, you can either save the String inside A :

 pub struct A { some_attr: String } 

or you can use std::borrow::Cow to store either the slice or its line:

 pub struct A<'a> { some_attr: Cow<'a, str> } 

In the latter case, your function might look like this:

 fn combine(orig: &str) -> A<'static> { let attr = orig.to_owned() + "suffix"; A { some_attr: attr.into() } } 

Note that since you are creating a string inside a function, it appears as an option belonging to Cow , and therefore you can use the 'static lifetime 'static parameter for the resulting value. It is also possible to bind it to orig , but there is no reason for this.

With Cow , you can also create A values ​​directly from slices without highlighting:

 fn new(orig: &str) -> A { A { some_attr: orig.into() } } 

Here, the parameter lifetime A will be linked (through the lifetime elision) to the lifetime of the slice of the input string. In this case, the borrowed version of Cow is used and no selection is made.

Also note that it is better to use to_owned() or into() to convert string slices to String , because these methods do not require formatting code to run and are therefore more efficient.

how can you bring life to 'static when you create it on the fly? Not sure what “native Cow variant” means and why it makes 'static .

Here is the definition of Cow :

 pub enum Cow<'a, B> where B: 'a + ToOwned + ?Sized { Borrowed(&'a B), Owned(B::Owned), } 

It looks complicated, but actually it is simple. A Cow instance may contain a reference to some type B or a value belonging to it, which can be obtained from B using the ToOwned attribute. Since str implements ToOwned , where the Owned associated type is String (written as ToOwned<Owned = String> when this enum is specialized for str , it looks like this:

 pub enum Cow<'a, str> { Borrowed(&'a str), Owned(String) } 

Consequently, Cow<str> can represent either a string slice or a string belonging to it - and although Cow does provide methods for the write cloning function, it is also often used to store a value that can be either borrowed or owned to avoid additional allocations. Since Cow<'a, B> implements Deref<Target = B> , you can get &B from Cow<'a, B> with a simple iteration: if x is Cow<str> , then &*x is &str , regardless of what is inside x - naturally, you can get a snippet from both variants of Cow .

You can see that the Cow::Owned variant does not contain links inside it, only String . Therefore, when the Cow value is created using the Owned option, you can select any lifetime you want (remember, life cycle parameters are very similar to type parameters, in particular, the one who selects them) there are no restrictions on it. Therefore, it makes sense to choose 'static as long as possible.

Does orig.to_owned ownership of the orig.to_owned ? It seems like this would be inconvenient.

The to_owned() method refers to the ToOwned :

 pub trait ToOwned { type Owned: Borrow<Self>; fn to_owned(&self) -> Self::Owned; } 

This attribute is implemented by str with Owned equal to String . to_owned() method returns a variant of any value it owns on which it is called. In this particular case, it creates a String from &str , effectively copying the contents of the slice of the string into the new distribution. Therefore, no, to_owned() does not imply transfer of ownership, it is more like a smart clone.

As far as I can tell, String implements Into<Vec<u8>> but not str , since we can call into() in the second example?

The Into feature is very versatile and implemented for many types in the standard library. Into usually implemented using the From : if T: From<U> attribute, then U: Into<T> . There are two important From implementations in the standard library:

 impl<'a> From<&'a str> for Cow<'a, str> impl<'a> From<String> for Cow<'a, str> 

These implementations are very simple - they simply return Cow::Borrowed(value) if value is &str and Cow::Owned(value) if value is String .

This means that &'a str and String implement Into<Cow<'a, str>> , and therefore they can be converted to Cow using the into() method. What happens in my example - I use into() to convert String or &str to Cow<str> . Without this explicit conversion, you will get an error about inconsistent types.

+13
source

All Articles