Question

How to Convert String to &'static str in Rust

Question

I want to convert a Rust String into a &str. More specifically, I need to understand whether it can be converted into a string slice with the 'static lifetime, written as &'static str.

For example, I am trying to work with owned string data and then use it where a static string slice is expected. What is the correct way to do this in Rust, and what are the limitations?

let s: String = String::from("hello");
// How can this become &str?
// Can it become &'static str?

Short Answer

By the end of this page, you will understand the difference between String, &str, and &'static str in Rust, why a normal String cannot usually become &'static str, and what safe alternatives to use. You will also see when borrowing is enough, when leaking memory is technically possible, and how real Rust code usually handles this situation.

Concept

String and &str are closely related in Rust, but they are not the same type.

  • String is an owned, growable string stored on the heap.
  • &str is a borrowed view into string data.
  • &'static str is a borrowed string slice that is guaranteed to live for the entire duration of the program.

A String can easily be borrowed as a &str because the String owns the text data.

let s = String::from("hello");
let slice: &str = &s;

This works because slice only borrows from s, and s is still alive.

The important part is the lifetime. A borrowed &str can only live as long as the data it points to. Since a normal String is dropped when it goes out of scope, any &str borrowed from it also becomes invalid at that point.

That is why you cannot normally turn a runtime-created String into &'static str. A 'static reference means the data must remain valid for the whole program, but an ordinary String does not.

The most common source of &'static str is a string literal:

let name: &'static str = "hello";

String literals are baked into the compiled program, so they naturally have the 'static lifetime.

Why this matters:

  • Rust uses lifetimes to prevent dangling references.
  • Understanding ownership and borrowing helps you choose the right type in APIs.
  • Many beginner errors in Rust come from expecting borrowed data to outlive its owner.

In practice, if you have a String, the usual answer is:

  • borrow it as &str when you only need a temporary view
  • keep it as String if ownership is needed
  • use &'static str only when the data is truly static or intentionally leaked

Mental Model

Think of a String as a notebook that you own.

A &str is like letting someone read a page from your notebook. They can read it only while your notebook still exists.

A &'static str is like text carved into stone in a public square. It stays there for the entire lifetime of the program.

So:

  • String = owned notebook
  • &str = borrowed page from the notebook
  • &'static str = permanent inscription

If you create a String at runtime, it is not automatically permanent. Borrowing from it does not make it permanent either. The borrowed slice is still tied to the owner.

Take Quiz

Syntax and Examples

Borrowing String as &str

This is the normal conversion:

let s = String::from("hello");
let slice1: &str = &s;
let slice2: &str = s.as_str();

println!("{} {}", slice1, slice2);

Both &s and s.as_str() give you a &str view into the string.

Why this is not &'static str

fn make_str() -> &str {
    let s = String::from("hello");
    s.as_str()
}

This does not compile, because s is dropped when the function ends. Returning a reference to it would create a dangling reference.

Returning an owned String instead

If the string is created at runtime, return String:

fn make_string() -> String {
    String::from("hello")
}

Using a string literal for &'static str

fn message() -> &'static str {
    "hello"
}

This works because string literals are static.

Intentionally leaking a String

Rust does allow you to convert a String into &'static str by leaking it:

fn make_static(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

fn main() {
    let s = String::from("hello");
    let static_str: &'static str = make_static(s);
    println!("{}", static_str);
}

This works, but the memory is never freed. It should only be used in special cases, such as initializing data that truly lives for the whole program.

Prefer shared ownership when appropriate

Sometimes you want long-lived string data, but not necessarily &'static str. In real programs, Arc<str> or String is often a better fit.

use std::sync::Arc;

let text: Arc<str> = Arc::from("hello".to_string());
println!("{}", text);

Step by Step Execution

Consider this example:

fn main() {
    let s = String::from("hello");
    let slice = s.as_str();
    println!("{}", slice);
}

Step by step:

  1. let s = String::from("hello");

    • A String is created on the heap.
    • The variable s owns that heap data.
  2. let slice = s.as_str();

    • slice becomes a &str borrowing the contents of s.
    • No new string is allocated.
    • slice points to the same text data owned by s.
  3. println!("{}", slice);

    • This is valid because s still exists.
  4. End of main

    • slice goes out of scope.
    • s is dropped.
    • The heap memory for the string is freed.

Now compare that with this invalid pattern:

fn bad() -> &str {
    let s = String::from("hello");
    s.as_str()
}

What happens here:

  1. s is created inside bad.
  2. s.as_str() creates a borrowed &str.
  3. The function tries to return that borrowed reference.
  4. But s is dropped immediately when the function ends.
  5. The returned reference would point to freed memory.
  6. Rust rejects this at compile time.

That is exactly what Rust's lifetime system is designed to prevent.

Real World Use Cases

Borrow a String as &str

This is common when calling functions that only need read-only access:

fn print_name(name: &str) {
    println!("{}", name);
}

let name = String::from("Ava");
print_name(&name);

Accept either String or &str

Many APIs take &str so callers can pass both string literals and borrowed String data.

fn is_empty_text(text: &str) -> bool {
    text.trim().is_empty()
}

Use &'static str for fixed messages

Useful for constant labels, error messages, and compile-time text:

fn status_label(code: u32) -> &'static str {
    match code {
        200 => "OK",
        404 => "Not Found",
        _ => "Unknown",
    }
}

Use owned strings for dynamic content

If content depends on user input, configuration, or database values, use String:

fn welcome(name: &str) -> String {
    format!("Welcome, {}!", name)
}

Leak only for program-long registries

In rare cases, applications build global lookup tables once and keep them forever. In those cases, leaking may be acceptable, but it should be a deliberate design choice.

Take Quiz

Real Codebase Usage

In real Rust projects, developers usually avoid forcing dynamic strings into &'static str.

Common patterns include:

1. Accept &str in function parameters

This is flexible and ergonomic.

fn log_message(msg: &str) {
    println!("LOG: {}", msg);
}

Callers can pass:

  • string literals
  • &String
  • string slices

2. Return String for computed text

If the function builds text dynamically, return ownership.

fn build_path(user: &str) -> String {
    format!("/home/{}", user)
}

3. Use &'static str for fixed enums or constants

const APP_NAME: &str = "my_app";

4. Use guard clauses with borrowed strings

fn validate(name: &str) -> Result<(), &'static str> {
    if name.trim().is_empty() {
        return Err("name cannot be empty");
    }
    Ok(())
}

Static error messages are a good fit for &'static str because they are fixed literals.

5. Prefer owned or shared types for stored dynamic data

For application state, config values, HTTP payloads, and parsed file contents, developers usually store:

  • String
  • Box<str>
  • Arc<str>
  • Cow<'a, str> when borrowing-or-owning flexibility is useful

6. Avoid unnecessary lifetime constraints

If you write an API requiring &'static str, you make it impossible to pass ordinary runtime strings. Real codebases often loosen this to &str or impl AsRef<str> unless true static data is required.

Common Mistakes

Mistake 1: Assuming &str always means 'static

&str does not automatically mean &'static str.

let s = String::from("hello");
let slice: &str = &s; // valid

This slice only lives as long as s does.

Mistake 2: Returning a borrowed slice from a local String

Broken code:

fn bad() -> &str {
    let s = String::from("hello");
    &s
}

Why it fails:

  • s is dropped at the end of the function
  • the returned reference would dangle

Fix: return String instead.

fn good() -> String {
    String::from("hello")
}

Mistake 3: Using &'static str when String is the right type

Broken design:

fn username() -> &'static str {
    let name = String::from("user123");
    Box::leak(name.into_boxed_str())
}

This compiles, but leaks memory every time it runs.

Fix: return String unless you truly need permanent storage.

Mistake 4: Over-constraining function parameters

Too strict:

fn greet(name: &'static str) {
    println!("Hello, {}", name);
}

This prevents passing many useful values.

Better:

fn greet(name: &str) {
    println!("Hello, {}", name);
}

Mistake 5: Thinking conversion changes ownership rules

Calling .as_str() does not create independent storage. It only borrows.

let s = String::from("hello");
let slice = s.as_str();

If s goes away, slice cannot outlive it.

Comparisons

TypeOwns data?Growable?Lifetime tied to owner?Typical use
StringYesYesNo borrowed lifetime neededDynamic text you create or store
&strNoNoYesTemporary borrowed string view
&'static strNoNoLives for whole programString literals and fixed messages
Box<str>YesNoNo borrowed lifetime neededOwned immutable string data

String vs &str

  • Use String when you need ownership.
  • Use &str when you only need to read text temporarily.

&str vs &'static str

  • &str can have any valid lifetime.
  • &'static str is only for data valid for the entire program.

String vs leaked &'static str

ChoiceProsCons
StringSafe, owned, flexibleNot static
Box::leak(... ) -> &'static strProduces &'static strMemory is never freed

&'static str vs String in APIs

API TypeBest for
fn f(msg: &str)Most general read-only string input
fn f() -> StringDynamically created output
fn f() -> &'static strFixed compile-time output
Take Quiz

Cheat Sheet

Quick rules

  • String -> &str: yes
  • String -> &'static str: not normally
  • String literal -> &'static str: yes
  • as_str() borrows; it does not extend lifetime

Common syntax

let s = String::from("hello");
let a: &str = &s;
let b: &str = s.as_str();

Static string literal

let msg: &'static str = "hello";

Return dynamic text

fn make_text() -> String {
    String::from("hello")
}

Return fixed text

fn label() -> &'static str {
    "ready"
}

Leak to force 'static

fn leak_string(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

Use this only when you intentionally want memory to live until program exit.

Good API defaults

  • input parameter: &str
  • computed return value: String
  • fixed constant return value: &'static str

Key idea

A reference cannot outlive the data it points to.

FAQ

Can I convert a String to &str in Rust?

Yes. Use &s or s.as_str().

Can I convert a String to &'static str safely?

Not in the normal sense. A runtime String does not live for the entire program, so it cannot naturally become &'static str.

Why do string literals have 'static lifetime?

Because they are stored in the program's compiled binary and exist for the whole runtime of the program.

What does as_str() do?

It returns a borrowed &str view into the String. It does not allocate and does not make the data static.

When should I return String instead of &str?

Return String when the text is created dynamically inside the function.

Is Box::leak a valid way to get &'static str?

Yes, but it leaks memory permanently. Use it only when that is an intentional design decision.

Should function parameters use &'static str?

Usually no. Prefer &str unless the function truly requires only program-long static text.

Take Quiz

Mini Project

Description

Build a small Rust program that formats a user-facing message and demonstrates the difference between borrowed string slices, owned strings, and static string literals. This project is useful because these three forms appear constantly in real Rust programs such as CLI tools, web handlers, and validators.

Goal

Create a program that accepts dynamic text, prints it through functions using &str, returns computed text as String, and returns fixed labels as &'static str.

Requirements

  • Write one function that accepts a &str and prints it.
  • Write one function that returns a dynamic String using format!.
  • Write one function that returns a fixed &'static str label.
  • In main, create a String and pass it as a &str.
  • Print the results of all three functions to show the differences clearly.
Take Quiz

Keep learning

Related questions

View more