R
RUSTY YELLOW PAGES

String literalLiteral


Explanation

A double-quoted string literal, such as "hello, world", produces a &'static str — a borrowed reference to UTF-8 text baked directly into the compiled binary, not an owned, heap-allocated String.

To get an owned, growable String, convert explicitly: "hello".to_string() or String::from("hello"). The literal itself is always &str. Escape sequences (\n, \t, \\, \", \u{...}, …) are processed inside a normal string literal — see escape sequences; use a raw string literal when you want backslashes taken literally.

Usage examples

Producing a &str from a literal

let msg = "connected"; // <- string literal: produces `&'static str`, not `String`

Working with text

A format! template is itself a string literal, with captured identifiers keeping the placeholder and its value next to each other.

fn format_status(sensor_id: &str, reading: f64) -> String {
    format!("sensor \"{sensor_id}\" reported {reading:.1}") // <- string literal: the format template itself
}

let msg = format_status("temp-01", 21.53);
assert_eq!(msg, "sensor \"temp-01\" reported 21.5");

Captured-identifier syntax ({sensor_id}) keeps the template and its values together instead of separated into positional arguments — the inline-capture form is documented in the std std::fmt docs and reads best once a template has more than a placeholder or two.

Designing a public API

A function parameter should ask for the least ownership it needs — &str accepts a literal directly, with no allocation, while still working for an owned String through auto-deref.

// PREFER: `&str` accepts a literal, a `String`, or a slice, no forced copy
pub fn greet(name: &str) -> String {
    format!("hello, {name}") // <- string literal: the format template
}

// AVOID: `String` forces every caller to allocate, even for a literal like "guest"
pub fn greet_owned(name: String) -> String {
    format!("hello, {name}")
}

let a = greet("guest");                     // <- string literal passed directly, no allocation
let b = greet_owned("guest".to_string());   // caller must allocate just to satisfy the signature

Accepting &str instead of String lets a caller pass a literal with zero allocation while still accepting an owned String where one already exists — the Book's "String Slices as Parameters" makes exactly this argument for preferring &str in a signature.

Handling and propagating errors

An error message built with a string literal template should include the offending input, not just describe the category of failure.

#[derive(Debug)]
struct ParseError {
    message: String,
}

fn parse_port(input: &str) -> Result<u16, ParseError> {
    input.parse::<u16>().map_err(|_| ParseError {
        message: format!("\"{input}\" is not a valid port number"), // <- string literal: the format template
    })
}

Including the offending value in the message gives the caller enough information to fix the problem without re-deriving it, which the Book's error-handling chapter treats as basic error-message hygiene.

Explanation

Embedded support: Full

A "..." literal needs nothing from alloc, only from core — it's 'static text already sitting in the binary's rodata section at boot, not data that gets constructed or copied anywhere at runtime. That's worth stating plainly because it cuts against a common misconception: #![no_std] does not mean "no strings," it means no String/heap allocation without opting in. A &str literal used for a log message, an error string, or a fixed label works exactly the same way, with the exact same zero-cost 'static guarantee, on a target with no heap configured at all. Where an owned, growable string is genuinely needed, alloc::string::String (behind the alloc crate) or a fixed-capacity heapless::String are the embedded substitutes — but the literal itself never requires either.

Usage examples

Logging a startup message with defmt, no heap involved

#![no_std]

use defmt::info;

fn log_startup() {
    info!("firmware started"); // <- string literal: `&'static str` baked into rodata, no heap involved
}

Formatting a status line into a heapless::String

use core::fmt::Write;
use heapless::String;

fn status_message(reading: f32) -> String<32> {
    let mut s: String<32> = String::new();
    let _ = write!(s, "temp: {reading:.1}C"); // <- string literal: the format template itself, still needs no heap
    s
}