Do string literals get deallocated?

fn some_func() -> &str {
  "abc"
}

Does the string literal get deallocated at any point in the program ?

No, but it never got allocated either. A string literal is “baked into” your program executable file just like the program’s instructions themself are. The &str will merely be an immutable reference to that data that exists at all time throughout program execution; your operating system placed it into the memory, together with all the program instructions, when it loaded your program, and it will release the memory when the program ends.

If you run some_func() multiple times, you’ll get multiple references to the same pre-existing memory. In fact, even different, unrelated functions that contain identical string literals "abc" will typically use the same shared “baked in” string data when the program runs, since the compiler can deduplicate those.


Your function doesn’t actually compile as-is, but it does if you are explicit about the lifetime parameter of the &str. A typical choice would be the following.

fn some_func() -> &'static str {
  "abc"
}

The 'static lifetime on the reference means that the data it points to will exist for the entire duration of program execution and never disappear.

An alternative approach to specify a lifetime is to be generic over lifetimes, i.e. [1]

fn some_func<'a>() -> &'a str {
  "abc"
}

Now, this function can be called with 'a = 'static, too, and in fact, since lifetimes don’t influence program behavior, the reference it returns will thus be guaranteed from the type signature alone to be always pointing to data that lives for the entire duration of the program. Note that the less-generic version of the function that returns &'static str is (almost) exactly as generally usable, because you can (implicitly) coerce the &'static str that it returns into a &'a str reference of any lifetime.


Edited to account for the fact that OP’s code doesn’t compile; somehow I missed that / never tested that. Thanks @tczajka for pointing that out below.


  1. note that being generic over lifetimes is a fairly natural thing, too, and it can often even happen implicitly; e.g. if your function signature was fn some_other_func(x: &str) -> &str, that would be a shorthand for fn some_other_func<'a>(x: &'a str) -> &'a str ↩︎

9 Likes

Actually this code doesn't compile.

It needs to be:

fn some_func() -> &'static str {
  "abc"
}
2 Likes

Am I right to understand that when you have a string literal like this, the bytes underlying the string value are literally part of the compiled program, and then your code just stores a pointer to wherever those bytes are in memory?

As a side question--this code implies that if you use the same string literal twice, it's stored at the same location; is that guaranteed, or just something that might happen?

fn main() {
    let a = "abcd";
    let b = "abcd";
    
    println!("{:p}", a);
    println!("{:p}", b);
}

(Rust Playground)

(Sorry to hijack the thread)

Check out the asm for your program here (.L__unnamed_1).

It's not guaranteed. If you actually need to care (extreme size limitations?), use a static.

1 Like

You can also use a program such as strings, or just a hex viewer, to literally see the bytes that comprise the string literals in a compiled binary.

2 Likes

Fun fact: when reverse engineering some unknown executable, one of the first things you'll probably do is run strings on the binary to see what string literals have been used. You can often find goodies like URLs and other hard-coded constants this way and is why crates such as obfstr exist.

For example, C's printf() function can be manipulated to write to arbitrary memory and you can quickly check if a program might be vulnerable by searching for something that looks like it would be used as a printf() formatting string).

3 Likes

Thanks for the info--I was more just curious, than actually having a real use for it. Although obfstr makes me think, now...