87

There are several questions that seem to be about the same problem I'm having. For example see here and here. Basically I'm trying to build a String in a local function, but then return it as a &str. Slicing isn't working because the lifetime is too short. I can't use str directly in the function because I need to build it dynamically. However, I'd also prefer not to return a String since the nature of the object this is going into is static once it's built. Is there a way to have my cake and eat it too?

Here's a minimal non-compiling reproduction:

fn return_str<'a>() -> &'a str {
    let mut string = "".to_string();

    for i in 0..10 {
        string.push_str("ACTG");
    }

    &string[..]
}
Community
  • 1
  • 1
anderspitman
  • 9,230
  • 10
  • 40
  • 61
  • 4
    Other questions have the same problem, and the answer is still the same: it is not possible to build a `String` in a function and return it as a `&str` because of Rust memory model. – Levans Apr 03 '15 at 08:50
  • 1
    Your reasoning for not wanting to return a `String` makes no sense to me. Just store a `String` in this "static" object instead of a `&str`. It's easier, it's at least as ergonomic, it makes more sense ownership-wise, and it doesn't even have any performance advantage. –  Apr 03 '15 at 10:28
  • @delnan You actually answered something else I was wondering about, which was whether using ``String`` has any performance disadvantages. I should be able to refactor to use ``String``s – anderspitman Apr 03 '15 at 17:48
  • 2
    Some may find this article helpful: [Strategies for Returning References in Rust](https://bryce.fisher-fleig.org/blog/strategies-for-returning-references-in-rust/index.html). – Jonathan Tran Jul 14 '18 at 17:12

7 Answers7

106

No, you cannot do it. There are at least two explanations why it is so.

First, remember that references are borrowed, i.e. they point to some data but do not own it, it is owned by someone else. In this particular case the string, a slice to which you want to return, is owned by the function because it is stored in a local variable.

When the function exits, all its local variables are destroyed; this involves calling destructors, and the destructor of String frees the memory used by the string. However, you want to return a borrowed reference pointing to the data allocated for that string. It means that the returned reference immediately becomes dangling - it points to invalid memory!

Rust was created, among everything else, to prevent such problems. Therefore, in Rust it is impossible to return a reference pointing into local variables of the function, which is possible in languages like C.

There is also another explanation, slightly more formal. Let's look at your function signature:

fn return_str<'a>() -> &'a str

Remember that lifetime and generic parameters are, well, parameters: they are set by the caller of the function. For example, some other function may call it like this:

let s: &'static str = return_str();

This requires 'a to be 'static, but it is of course impossible - your function does not return a reference to a static memory, it returns a reference with a strictly lesser lifetime. Thus such function definition is unsound and is prohibited by the compiler.

Anyway, in such situations you need to return a value of an owned type, in this particular case it will be an owned String:

fn return_str() -> String {
    let mut string = String::new();

    for _ in 0..10 {
        string.push_str("ACTG");
    }

    string
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • 8
    Technically you can return a `String` as `'static` as long as you leak the `String`. (With `std::mem::forget`), But it will remain allocated forever. (Which I guess the the point in `'static`) – Triss Healy Jun 30 '17 at 08:12
  • 1
    Rust is really smart, so why doesn't it see, when I create a variable locally and try to return a reference to it, that I want to return it without copying and just not drop it? – dscham Nov 25 '22 at 11:22
  • 2
    @Sens all local variables are allocated in the stack frame of the currently executing function. When the function returns, the stack frame is freed, along with all its contents. Therefore, if you return a reference to one of the local variables, it will become invalid right after the function returns. The only workaround for this would be to allocate such a local variable not on the stack but elsewhere and return a reference to _that_, but this approach goes against various of Rust's mechanics (for example, it is now unclear who owns this value). Other languages with GC do precisely this, btw. – Vladimir Matveev Jan 06 '23 at 23:05
  • @VladimirMatveev okay, that makes sense. I tend to forget the stack/heap seperation. Coming from GCed languages. Still, shouldn't the compiler see the very common use-case and generate machine code to copy the return value from stack to heap? Or is that too much magic for Rustlings?(meant in a loving way) Edit: Whoops, I just realised that's exaclty what you have to do manually. And that this is the appropriate behaviour for a low-level language. – dscham Jan 27 '23 at 14:39
  • 1
    @Sens yup, that would be too much magic. Go does this, for example, but Rust has chosen not to do this explicitly. It also makes the type system more orthogonal and sound: there is a strong semantic difference between say `&String` and `String`, so to make it work, there should either be an auto-conversion from `&String` to `String`, or `&String` and other reference types would have to handle being transferred to heap somehow, which would require some complex changes to the type system, I imagine. So not doing anything fancy is also the simplest approach here) – Vladimir Matveev Feb 02 '23 at 04:39
19

In certain cases, you are passed a string slice and may conditionally want to create a new string. In these cases, you can return a Cow. This allows for the reference when possible and an owned String otherwise:

use std::borrow::Cow;

fn return_str<'a>(name: &'a str) -> Cow<'a, str> {
    if name.is_empty() {
        let name = "ACTG".repeat(10);
        name.into()
    } else {
        name.into()
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    Holy cow, I was beginning to think that my use case (conditionally either passing through a string reference or modifying the string if needed) wasn't possible until I finally stumbled on this. – ijoseph Apr 18 '22 at 01:02
9

You can choose to leak memory to convert a String to a &'static str:

fn return_str() -> &'static str {
    let string = "ACTG".repeat(10);

    Box::leak(string.into_boxed_str())
}

This is a really bad idea in many cases as the memory usage will grow forever every time this function is called.

If you wanted to return the same string every call, see also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
4

The problem is that you are trying to create a reference to a string that will disappear when the function returns.

A simple solution in this case is to pass in the empty string to the function. This will explicitly ensure that the referred string will still exist in the scope where the function returns:

fn return_str(s: &mut String) -> &str {

    for _ in 0..10 {
        s.push_str("ACTG");
    }

    &s[..]
}

fn main() {
    let mut s = String::new();
    let s = return_str(&mut s);
    assert_eq!("ACTGACTGACTGACTGACTGACTGACTGACTGACTGACTG", s);
}

Code in Rust Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2499ded42d3ee92d6023161fe82e9b5f

Samuel Fekete
  • 314
  • 2
  • 5
2

This is an old question but a very common one. There are many answers but none of them addresses the glaring misconception people have about the strings and string slices, which stems from not knowing their true nature.

But lets start with the obvious question before addressing the implied one: Can we return a reference to a local variable?

What we are asking to achieve is the textbook definition of a dangling pointer. Local variables will be dropped when the function completes its execution. In other words they will be pop off the execution stack and any reference to the local variables then on will be pointing to some garbage data.

Best course of action is either returning the string or its clone. No need to obsess over the speed.

However, I believe the essence of the question is if there is a way to convert a String into an str? The answer is no and this is where the misconception lies:

You can not turn a String into an str by borrowing it. Because a String is heap allocated. If you take a reference to it, you still be using heap allocated data but through a reference. str, on the other hand, is stored directly in the data section of the executable file and it is static. When you take a reference to a string, you will get matching type signature for common string manipulations, not an actual &str.

You can check out this post for detailed explanation:

What are the differences between Rust's `String` and `str`?

Now, there may be a workaround for this particular use case if you absolutely use static text:

Since you use combinations of four bases A, C, G, T, in groups of four, you can create a list of all possible outcomes as &str and use them through some data structure. You will jump some hoops but certainly doable.

snnsnn
  • 10,486
  • 4
  • 39
  • 44
0

if it is possible to create the resulting STRING in a static way at compile time, this would be a solution without memory leaking

#[macro_use]
extern crate lazy_static;
    
fn return_str<'a>() -> &'a str {
    lazy_static! {
        static ref STRING: String = {
            "ACTG".repeat(10)
        };
    }

    &STRING
}
Kaplan
  • 2,572
  • 13
  • 14
-2

Yes you can - the method replace_range provides a work around -

let a = "0123456789";
//println!("{}",a[3..5]);  fails - doesn't have a size known at compile-time
let mut b = String::from(a);
b.replace_range(5..,"");
b.replace_range(0..2,"");
println!("{}",b); //succeeds 

It took blood sweat and tears to achieve this!