Skip to content

Commit dbcea32

Browse files
natecook1000xwu
andauthored
Add a proposal for a string interpolation default parameter (#2772)
* Add a proposal for the string interpolation default parameter * Claim SE-0477 and prepare for review --------- Co-authored-by: Xiaodi Wu <[email protected]>
1 parent 224f4fc commit dbcea32

File tree

1 file changed

+171
-0
lines changed

1 file changed

+171
-0
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Default Value in String Interpolations
2+
3+
* Proposal: [SE-0477](0477-default-interpolation-values.md)
4+
* Authors: [Nate Cook](https://github.com/natecook1000)
5+
* Review Manager: [Xiaodi Wu](https://github.com/xwu)
6+
* Status: **Active review (April 16...29, 2025)**
7+
* Implementation: [swiftlang/swift#80547](https://github.com/swiftlang/swift/pull/80547)
8+
* Review: ([pitch](https://forums.swift.org/t/pitch-default-values-for-string-interpolations/69381))
9+
10+
## Introduction
11+
12+
A new string interpolation syntax for providing a default string
13+
when interpolating an optional value.
14+
15+
## Motivation
16+
17+
String interpolations are a streamlined and powerful way to include values within a string literal.
18+
When one of those values is optional, however,
19+
interpolating is not so simple;
20+
in many cases, a developer must fall back to unpalatable code
21+
or output that exposes type information.
22+
23+
For example,
24+
placing an optional string in an interpolation
25+
yields an important warning and two suggested fixes,
26+
only one of which is ideal:
27+
28+
```swift
29+
let name: String? = nil
30+
print("Hello, \(name)!")
31+
// warning: string interpolation produces a debug description for an optional value; did you mean to make this explicit?
32+
// print("Hello, \(name)!")
33+
// ^~~~
34+
// note: use 'String(describing:)' to silence this warning
35+
// print("Hello, \(name)!")
36+
// ^~~~
37+
// String(describing: )
38+
// note: provide a default value to avoid this warning
39+
// print("Hello, \(name)!")
40+
// ^~~~
41+
// ?? <#default value#>
42+
43+
```
44+
45+
The first suggestion, adding `String(describing:)`,
46+
silences the warning but includes `nil` in the output of the string —
47+
maybe okay for a quick shell script,
48+
but not really appropriate result for anything user-facing.
49+
50+
The second suggestion is good,
51+
allowing us to provide whatever default string we'd like:
52+
53+
```swift
54+
let name: String? = nil
55+
print("Hello, \(name ?? "new friend")!")
56+
```
57+
58+
However, the nil-coalescing operator (`??`)
59+
only works with values of the same type as the optional value,
60+
making it awkward or impossible to use when providing a default for non-string types.
61+
In this example, the `age` value is an optional `Int`,
62+
and there isn't a suitable integer to use when it's `nil`:
63+
64+
```swift
65+
let age: Int? = nil
66+
print("Your age: \(age)")
67+
// warning, etc....
68+
```
69+
70+
To provide a default string when `age` is missing,
71+
we have to write some gnarly code,
72+
or split out the missing case altogether:
73+
74+
```swift
75+
let age: Int? = nil
76+
// Optional.map
77+
print("Your age: \(age.map { "\($0)" } ?? "missing")")
78+
// Ternary expression
79+
print("Your age: \(age != nil ? "\(age!)" : "missing")")
80+
// if-let statement
81+
if let age {
82+
print("Your age: \(age)")
83+
} else {
84+
print("Your age: missing")
85+
}
86+
```
87+
88+
## Proposed solution
89+
90+
The standard library should add a string interpolation overload
91+
that lets you write the intended default as a string,
92+
no matter what the type of value:
93+
94+
```swift
95+
let age: Int? = nil
96+
print("Your age: \(age, default: "missing")")
97+
// Prints "Your age: missing"
98+
```
99+
100+
This addition will improve the clarity of code that uses string interpolations
101+
and encourage developers to provide sensible defaults
102+
instead of letting `nil` leak into string output.
103+
104+
## Detailed design
105+
106+
The implementation of this new interpolation overload looks like this,
107+
added as an extension to the `DefaultStringInterpolation` type:
108+
109+
```swift
110+
extension DefaultStringInterpolation {
111+
mutating func appendInterpolation<T>(
112+
_ value: T?,
113+
default: @autoclosure () -> String
114+
) {
115+
if let value {
116+
self.appendInterpolation(value)
117+
} else {
118+
self.appendInterpolation(`default`())
119+
}
120+
}
121+
}
122+
```
123+
124+
The new interpolation's `default:` parameter name
125+
matches the one in the `Dictionary` subscript that has a similar purpose.
126+
127+
You can try this out yourself by copy/pasting the snippet above into a project or playground,
128+
or by experimenting with [this Swift Fiddle](https://swiftfiddle.com/nxttprythnfbvlm4hwjyt2jbjm).
129+
130+
## Source compatibility
131+
132+
This proposal adds one new API to the standard library,
133+
which should not be source-breaking for any existing projects.
134+
If a project or a dependency has added a similar overload,
135+
it will take precedence over the new standard library API.
136+
137+
## ABI compatibility
138+
139+
This proposal is purely an extension of the ABI of the
140+
standard library and does not change any existing features.
141+
142+
## Implications on adoption
143+
144+
The new API will be included in a new version of the Swift runtime,
145+
and is marked as backward deployable.
146+
147+
## Future directions
148+
149+
There are [some cases][reflecting] where a `String(reflecting:)` conversion
150+
is more appropriate than the `String(describing:)` normally used via string interpolation.
151+
Additional string interpolation overloads could make it easier to use that alternative conversion,
152+
and to provide a default when working with optional values.
153+
154+
[reflecting]: https://forums.swift.org/t/pitch-default-values-for-string-interpolations/69381/58
155+
156+
## Alternatives considered
157+
158+
**An interpolation like `"\(describing: value)"`**
159+
This alternative would provide a shorthand for the first suggested fix,
160+
using `String(describing:)`.
161+
Unlike the solution proposed,
162+
this kind of interpolation doesn't make it clear that you're working with an optional value,
163+
so you could end up silently including `nil` in output without expecting it
164+
(which is the original reason for the compiler warnings).
165+
166+
**Extend `StringInterpolationProtocol` instead**
167+
The proposed new interpolation works with _any_ optional value,
168+
but some types only accept a limited or constrained set of types interpolations.
169+
If the new `\(_, default:)` interpolation proves to be a useful pattern,
170+
other types can add it as appropriate.
171+

0 commit comments

Comments
 (0)