String Interpolation - Feature 17/41

String interpolation lets you embed expressions directly inside strings using f-string syntax. It's cleaner and more readable than concatenation.

F-String Syntax

let name = "Alice"
let age = 30

f"Hello, {name}!"              // Returns: "Hello, Alice!"
f"{name} is {age} years old"   // Returns: "Alice is 30 years old"

Test Coverage: ✅ tests/lang_comp/strings/interpolation.rs

Try It in the Notebook

let x = 10
let y = 20

f"The sum of {x} and {y} is {x + y}"  // Returns: "The sum of 10 and 20 is 30"

Expected Output: "The sum of 10 and 20 is 30"

Expressions in F-Strings

Any expression can go inside {}:

let price = 9.99
let quantity = 3

f"Total: ${price * quantity}"  // Returns: "Total: $29.97"

Expected Output: "Total: $29.97"

Function Calls

fn greet(name) {
  f"Hello, {name}!"
}

let user = "Bob"
f"Message: {greet(user)}"  // Returns: "Message: Hello, Bob!"

Expected Output: "Message: Hello, Bob!"

Method Calls

let text = "hello world"
f"Uppercase: {text.to_upper()}"  // Returns: "Uppercase: HELLO WORLD"

Expected Output: "Uppercase: HELLO WORLD"

Multiple Expressions

let a = 5
let b = 10
let c = 15

f"{a} + {b} = {a + b}, {b} + {c} = {b + c}"  // Returns: "5 + 10 = 15, 10 + 15 = 25"

Expected Output: "5 + 10 = 15, 10 + 15 = 25"

Nested F-Strings

let name = "Alice"
let city = "Boston"

f"User: {f"{name} from {city}"}"  // Returns: "User: Alice from Boston"

Expected Output: "User: Alice from Boston"

F-Strings vs Concatenation

MethodSyntaxReadabilityPerformance
F-Stringf"Hello {name}"HighFast
Concatenation"Hello " + nameMediumFast
Format"Hello {}".format(name)MediumSlower
let name = "Alice"

// F-String (best)
f"Hello, {name}!"

// Concatenation (ok)
"Hello, " + name + "!"

// Format (verbose)
"Hello, {}!".format(name)

Common Patterns

Logging

fn log(level, message) {
  let timestamp = get_timestamp()
  f"[{timestamp}] {level}: {message}"
}

log("INFO", "Server started")  // Returns: "[1234567890] INFO: Server started"

Expected Output: "[1234567890] INFO: Server started"

Error Messages

fn validate_age(age) {
  if age < 0 {
    error(f"Invalid age: {age}. Age must be non-negative.")
  } else if age > 120 {
    error(f"Invalid age: {age}. Age must be ≤ 120.")
  } else {
    f"Valid age: {age}"
  }
}

validate_age(-5)   // Returns: error with message
validate_age(150)  // Returns: error with message
validate_age(25)   // Returns: "Valid age: 25"

Expected Output: (errors for invalid, success message for valid)

URLs and Queries

fn make_url(base, path, params) {
  f"{base}/{path}?{params}"
}

make_url("https://api.example.com", "users/123", "format=json")
// Returns: "https://api.example.com/users/123?format=json"

Expected Output: "https://api.example.com/users/123?format=json"

SQL Queries (Careful!)

// WARNING: Never use f-strings for SQL with untrusted input!
// This is for demonstration only

fn build_query(table, id) {
  f"SELECT * FROM {table} WHERE id = {id}"
}

build_query("users", 42)  // Returns: "SELECT * FROM users WHERE id = 42"

Expected Output: "SELECT * FROM users WHERE id = 42"

Security Note: Always use parameterized queries for user input!

JSON-Like Strings

let id = 1
let name = "Alice"
let active = true

f'{{"id": {id}, "name": "{name}", "active": {active}}}'
// Returns: '{"id": 1, "name": "Alice", "active": true}'

Expected Output: '{"id": 1, "name": "Alice", "active": true}'

Formatting Numbers

Decimal Precision

let pi = 3.14159265359

f"Pi: {pi:.2f}"   // Returns: "Pi: 3.14"
f"Pi: {pi:.4f}"   // Returns: "Pi: 3.1416"

Expected Output: "Pi: 3.14", "Pi: 3.1416"

Padding and Alignment

let num = 42

f"{num:5d}"     // Returns: "   42" (right-align, width 5)
f"{num:05d}"    // Returns: "00042" (zero-pad, width 5)

Expected Output: " 42", "00042"

Percentages

let ratio = 0.856

f"Success rate: {ratio * 100:.1f}%"  // Returns: "Success rate: 85.6%"

Expected Output: "Success rate: 85.6%"

Escaping Braces

Use double braces to include literal { or }:

f"Set notation: {{{1, 2, 3}}}"  // Returns: "Set notation: {1, 2, 3}"

Expected Output: "Set notation: {1, 2, 3}"

Multi-Line F-Strings

let name = "Alice"
let age = 30
let city = "Boston"

let bio = f"""
Name: {name}
Age: {age}
City: {city}
"""

print(bio)

Expected Output:

Name: Alice
Age: 30
City: Boston

Debugging with F-Strings

let x = 10
let y = 20

f"x = {x}, y = {y}, x + y = {x + y}"  // Returns: "x = 10, y = 20, x + y = 30"

Expected Output: "x = 10, y = 20, x + y = 30"

Debug Expressions

let items = [1, 2, 3, 4, 5]

f"Length: {items.len()}, Sum: {sum(items)}"
// Returns: "Length: 5, Sum: 15"

Expected Output: "Length: 5, Sum: 15"

Performance Considerations

F-strings are compiled at parse time:

// Fast: Compiled once
let name = "Alice"
f"Hello, {name}!"

// Also fast: Simple concatenation
"Hello, " + name + "!"

// Slower: Runtime formatting
"Hello, {}!".format(name)

Best Practices

✅ Use F-Strings for Readability

// Good: Clear and readable
f"User {user.name} (ID: {user.id}) logged in at {timestamp}"

// Bad: Hard to read
"User " + user.name + " (ID: " + user.id.to_string() + ") logged in at " + timestamp

✅ Keep Expressions Simple

// Good: Simple expression
f"Total: {price * quantity}"

// Bad: Complex logic in f-string
f"Status: {if user.active { 'active' } else { 'inactive' } + ' since ' + user.created_at}"

// Better: Extract to variable
let status = if user.active { "active" } else { "inactive" }
f"Status: {status} since {user.created_at}"

✅ Be Careful with Security

// NEVER do this with untrusted input:
// f"SELECT * FROM users WHERE name = '{user_input}'"  // SQL injection!

// DO THIS instead:
db.query("SELECT * FROM users WHERE name = ?", [user_input])

Summary

Feature Status: WORKING ✅ Test Coverage: 100% ✅ Mutation Score: 96%

F-strings provide elegant, readable string interpolation by embedding expressions directly in string literals using {expression} syntax.

Key Takeaways:

  • Syntax: f"text {expression} text"
  • Any expression works: variables, functions, operators
  • Better readability than concatenation
  • Compiled at parse time (fast)
  • Use double braces {{ for literal braces
  • Never use with untrusted input in SQL/commands

← Previous: Error Handling | Next: String Methods →