Return local String as a slice (&str)Return local String as a slice (&str) - Solution Checker - solutionschecker.com - Find the solution for any programming question. We as a solution checker will focus on finding the fastest possible solution for developers. Main topics like coding, learning.

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[..]
}

Solution 1

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
}

Solution 2

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()
    }
}

Solution 3

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:

Solution 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

Solution 5

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
}

Solution 6

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!