0

Inside a function that returns a &'a str, I created a String. I want to return a &'a str with the same contents than this String

fn givesString(A: &str) -> String {
    String::from("example")
}

pub struct S<'a> {
    pub strField: &'a str,
}
impl<'a> S<'a> {
    pub fn from_String_A(A: &'a str) -> Result<Self, Box<dyn std::error::Error>> {
        let processed_String = givesString(&A);
        let processed_str: &'a str = a_processed_String.as_str();

        Ok(Self {
            strField: processed_str,
        })
    }
}

playground

which doesn't compile: the borrowed value a_processed_String does not live long enough, since it's dropped at the end of the function.

I understand the String will be dropped no matter what.

But now, if I create a &'a str and pass it into the return value, it compiles with no issue:

fn givesString(A: &str) -> String {
    String::from("example")
}

pub struct S<'a> {
    pub strField: &'a str,
}
impl<'a> S<'a> {
    pub fn from_String_A(A: &'a str) -> Result<Self, Box<dyn std::error::Error>> {
        let longlife: &'a str = "hello from the outside of this fn";

        Ok(Self {
            strField: longlife,
        })
    }
}

playground

So, is it possible to create a new &'a str that, instead of borrowing from the String that will be dropped, simply points to some memory with a lifetime 'a that holds the same contents as the String ?

There has been countless similar questions where the answer was to simply return a String instead, but let's assume I cannot touch the code for givesString or the struct S

Nicolas Marshall
  • 4,186
  • 9
  • 36
  • 54
  • 1
    "I'm trying to have a function return a &'a str from a String that's created inside the function:" and so where the string will live ? you can't return local value in any language I know. – Stargateur Nov 21 '19 at 18:08
  • The String won't live. I'm only interested in creating a `&'a str` with the same contents – Nicolas Marshall Nov 21 '19 at 18:28
  • Or create a long-lived clone of the first apple, throw the first apple and give people the second one. – Nicolas Marshall Nov 21 '19 at 18:51
  • yeah we call that apple, so in this context a STRING – Stargateur Nov 21 '19 at 18:54
  • 2
    One useful thing to remember with Rust is that lifetimes are descriptive and not prescriptive. Apart from `static`s which live forever, you can't declare that you want a particular object to have a particular lifetime. – turbulencetoo Nov 21 '19 at 20:40
  • Another way to look at it is that references should be passed "up the stack", not "down the stack". You have a value on lower end, you push reference upwards, another function that takes hold of reference might even push it higher... But reference can never be lower than the value itself. Not from the standpoint of implementation, but logic. Value should always be popped off ***after*** all references are. To avoid the impossibility of moving reference below the value in the stack... We create a new value and now we can put it anywhere, even move it downwards. You can never sanely avoid that. –  Nov 23 '19 at 10:18
  • @Sahsahae that's a really useful way to picture it, will keep it in mind. If I create a String on the top of the stack and pass it down when the function returns, I suppose it's the exact same memory being re-used ? – Nicolas Marshall Nov 23 '19 at 13:35
  • You'd have to look into what code LLVM generates to know what exactly happens. String struct itself is very cheap to copy, and actual ***string*** is on the heap. And what you're trying to avoid, as I understand, is allocation on the heap, that's simply not possible in this case. –  Nov 23 '19 at 19:59

1 Answers1

1

This depends on how hard you need to try...

In the second example, longlife is a literal &'static str. Since 'static lives for at least as long as any 'a no matter what 'a is, it's valid to be assigned to the return value via S. This is not very useful in the general case, though, since you can't modify a &'static str for obvious reasons.

I'd highly recommend to re-structure the code so there is an owner, S seems to be a candidate.

You can make this work via std::mem::forget: You create the String, forget about it and manually derive a forced &'static str from it. This quite simly means you leak the memory of the allocated String, making it live for at least as long as any 'a. Most people would consider this simply a bug or at least a really ugly, ugly hack. But it's possible.

user2722968
  • 13,636
  • 2
  • 46
  • 67
  • "In the second example, longlife is a literal &'static str." not exactly, longlife have `'a` lifetime but in the line is assigned to a static str, still longlife doesn't have a static lifetime. – Stargateur Nov 21 '19 at 18:44
  • "This quite simly means you leak the memory of the allocated String, making it live for at least as long as any 'a" a leak is a leak, it will live as long as the program. – Stargateur Nov 21 '19 at 18:44
  • In the real code I will certainly change `strField` to a `String`. In other questions answers suggested returning the String, which is different than creating a `&'a str`. So I was still curious to know if this was impossible, or simply not the recommended way to do in other questions' cases. Also it didn't occur to me that `longlife` was `&'static`, so thanks. By the way creating a `&'static` like `longlife` each time would re-use the same memory and not leak more over time ? – Nicolas Marshall Nov 21 '19 at 18:48
  • Stargateur is correct that `longlife` is a `&'a str` which gets a `&'static str` assigned. Same argument applies: Since `'static` outlives any `'a`, the assignment is always sound. – user2722968 Nov 21 '19 at 19:18
  • A literal like `let longlife = "foobar"` will always re-use the same memory and not leak over time. The literal is baked into the executable and, therefor, not even allocated on the heap in the first place – user2722968 Nov 21 '19 at 19:19