interp
import std::fmt::interp; — injected by the compiler into any module containing an f-string · source
f-strings
function fmt_new() -> String;
function fmt_append_lit(out: String, text: string) -> String;
function fmt_append_display<T>(out: String, value: &T) -> String
where T: Display;
function fmt_append_debug<T>(out: String, value: &T) -> String
where T: Debug;
function fmt_append_fmt<T>(out: String, value: &T, spec: string) -> String
where T: Display;
An f-string builds an owned String. Each {expr} hole is rendered through Display and each {expr:?} hole through Debug:
const line: String = f"user {name} has {count} items";
const dump: String = f"state = {state:?}";
The parser lowers the literal into a chain of calls threading one String builder left to right:
// f"x = {x}, y = {y:?}" becomes
fmt_append_debug(
fmt_append_lit(
fmt_append_display(
fmt_append_lit(fmt_new(), "x = "),
&x),
", y = "),
&y)
Each helper takes the partial string by value and returns it, so the whole expression evaluates to a single owned String with no intermediate temporaries to drop. The generic fmt_append_display / fmt_append_debug carry the Display / Debug bound so bound-directed dispatch resolves the nested value.fmt(f) to the right trait. A hole with a format spec — {n:>8} — goes through fmt_append_fmt, which hands the spec text to fmt::spec at runtime.
The compiler injects import std::fmt::interp; into any module that contains an f-string, so f"..." works with no import of your own. You never call these by hand.