Skip to content

Add a proposal for a string interpolation default parameter #2772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 16, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions proposals/0477-default-interpolation-values.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Default Value in String Interpolations

* Proposal: [SE-0477](0477-default-interpolation-values.md)
* Authors: [Nate Cook](https://github.com/natecook1000)
* Review Manager: [Xiaodi Wu](https://github.com/xwu)
* Status: **Active review (April 16...29, 2025)**
* Implementation: [swiftlang/swift#80547](https://github.com/swiftlang/swift/pull/80547)
* Review: ([pitch](https://forums.swift.org/t/pitch-default-values-for-string-interpolations/69381))

## Introduction

A new string interpolation syntax for providing a default string
when interpolating an optional value.

## Motivation

String interpolations are a streamlined and powerful way to include values within a string literal.
When one of those values is optional, however,
interpolating is not so simple;
in many cases, a developer must fall back to unpalatable code
or output that exposes type information.

For example,
placing an optional string in an interpolation
yields an important warning and two suggested fixes,
only one of which is ideal:

```swift
let name: String? = nil
print("Hello, \(name)!")
// warning: string interpolation produces a debug description for an optional value; did you mean to make this explicit?
// print("Hello, \(name)!")
// ^~~~
// note: use 'String(describing:)' to silence this warning
// print("Hello, \(name)!")
// ^~~~
// String(describing: )
// note: provide a default value to avoid this warning
// print("Hello, \(name)!")
// ^~~~
// ?? <#default value#>

```

The first suggestion, adding `String(describing:)`,
silences the warning but includes `nil` in the output of the string —
maybe okay for a quick shell script,
but not really appropriate result for anything user-facing.

The second suggestion is good,
allowing us to provide whatever default string we'd like:

```swift
let name: String? = nil
print("Hello, \(name ?? "new friend")!")
```

However, the nil-coalescing operator (`??`)
only works with values of the same type as the optional value,
making it awkward or impossible to use when providing a default for non-string types.
In this example, the `age` value is an optional `Int`,
and there isn't a suitable integer to use when it's `nil`:

```swift
let age: Int? = nil
print("Your age: \(age)")
// warning, etc....
```

To provide a default string when `age` is missing,
we have to write some gnarly code,
or split out the missing case altogether:

```swift
let age: Int? = nil
// Optional.map
print("Your age: \(age.map { "\($0)" } ?? "missing")")
// Ternary expression
print("Your age: \(age != nil ? "\(age!)" : "missing")")
// if-let statement
if let age {
print("Your age: \(age)")
} else {
print("Your age: missing")
}
```

## Proposed solution

The standard library should add a string interpolation overload
that lets you write the intended default as a string,
no matter what the type of value:

```swift
let age: Int? = nil
print("Your age: \(age, default: "missing")")
// Prints "Your age: missing"
```

This addition will improve the clarity of code that uses string interpolations
and encourage developers to provide sensible defaults
instead of letting `nil` leak into string output.

## Detailed design

The implementation of this new interpolation overload looks like this,
added as an extension to the `DefaultStringInterpolation` type:

```swift
extension DefaultStringInterpolation {
mutating func appendInterpolation<T>(
_ value: T?,
default: @autoclosure () -> String
) {
if let value {
self.appendInterpolation(value)
} else {
self.appendInterpolation(`default`())
}
}
}
```

The new interpolation's `default:` parameter name
matches the one in the `Dictionary` subscript that has a similar purpose.

You can try this out yourself by copy/pasting the snippet above into a project or playground,
or by experimenting with [this Swift Fiddle](https://swiftfiddle.com/nxttprythnfbvlm4hwjyt2jbjm).

## Source compatibility

This proposal adds one new API to the standard library,
which should not be source-breaking for any existing projects.
If a project or a dependency has added a similar overload,
it will take precedence over the new standard library API.

## ABI compatibility

This proposal is purely an extension of the ABI of the
standard library and does not change any existing features.

## Implications on adoption

The new API will be included in a new version of the Swift runtime,
and is marked as backward deployable.

## Future directions

There are [some cases][reflecting] where a `String(reflecting:)` conversion
is more appropriate than the `String(describing:)` normally used via string interpolation.
Additional string interpolation overloads could make it easier to use that alternative conversion,
and to provide a default when working with optional values.

[reflecting]: https://forums.swift.org/t/pitch-default-values-for-string-interpolations/69381/58

## Alternatives considered

**An interpolation like `"\(describing: value)"`**
This alternative would provide a shorthand for the first suggested fix,
using `String(describing:)`.
Unlike the solution proposed,
this kind of interpolation doesn't make it clear that you're working with an optional value,
so you could end up silently including `nil` in output without expecting it
(which is the original reason for the compiler warnings).

**Extend `StringInterpolationProtocol` instead**
The proposed new interpolation works with _any_ optional value,
but some types only accept a limited or constrained set of types interpolations.
If the new `\(_, default:)` interpolation proves to be a useful pattern,
other types can add it as appropriate.