Skip to content

§9 String Interpolation

let name = "Sailfin";
let greeting = "Hello, ${ name }!"; // "Hello, Sailfin!"
let math = "2 + 2 = ${ 2 + 2 }"; // "2 + 2 = 4"
let nested = "User: ${ user.name.trim() }"; // arbitrary expressions

${ expr } opens an interpolation. Whitespace at the edges is insignificant: ${name} and ${ name } are equivalent. A bare $ not followed by { is literal ("Total: $5"). Nested braces inside the expression are matched by depth, so "${ f({a: 1}) }" parses as a single interpolation. The compiler lowers interpolated strings into segment arrays evaluated at runtime.

The older {{ expr }} form is deprecated but still accepted during a migration window; using it emits a non-fatal W0212 deprecation warning at sfn check time. It will be removed in a later phase.

A literal-${ escape (\${) is not yet available — it is deferred to a follow-up (SFEP-0057 Phase 4 / SFN-483) — so a string that needs a literal ${ should currently be assembled another way (e.g. concatenation) rather than relying on an escape.

Primitive optional unions such as int | null render the active non-null payload in direct, flow-narrowed, and match-bound positions.

Concatenating a numeric or boolean operand (+)

Section titled “Concatenating a numeric or boolean operand (+)”

string + <int | float | bool> (either operand order) concatenates by stringifying the non-string operand, rather than requiring an explicit cast:

let n = 42;
let f = 1.5;
let t = true;
let a = "n=" + n; // "n=42"
let b = "f=" + f; // "f=1.5"
let c = "t=" + t; // "t=1"
let d = n + " items"; // "42 items" -- works with the string on either side

This is defined as sugar for string + (x as string) — the same number.to_string lowering the as string cast uses — so the two can never diverge. Booleans render "1"/"0", not "true"/"false", matching the as string cast rather than string interpolation’s "true"/"false" rendering (see above). This is easy to get wrong when porting code from interpolation to concatenation — check for it explicitly.

Indexing a string still yields a single-character string (s[i]), not a numeric code point, so concatenating an indexed character ("c=" + s[1]) is unaffected and continues to concatenate the character itself.

Raw pointer arithmetic (*u8 + int) is a distinct operation and is not concatenation — it still lowers to pointer offsetting, never stringification.