Trailing commas beyond arrays in Swift 6.1

⋅ 3 min read ⋅ Swift Swift Evolution

Table of Contents

We have always been able to leave a trailing comma in an array literal.

let numbers = [
1,
2,
3,
]

That last comma is doing a job. Add a fourth element and git sees one changed line:

     1,
2,
3,
+ 4,
]

Take the trailing comma away and the same edit touches two lines, because 3 has to become 3, before 4 can follow it:

     1,
2,
- 3
+ 3,
+ 4
]

The extra line is noise in review, and it is a conflict waiting to happen — two people appending to the same list both edit that line.

Everywhere else in Swift, we were stuck with the second version.

func makeCoffee(
beans: Beans,
grind: Grind,
water: Water // adding a parameter touches this line too
) { }

SE-0439, introduced in Swift 6.1, extends trailing commas to almost every other comma-separated list.

func makeCoffee(
beans: Beans,
grind: Grind,
water: Water,
) { }

Where it works now

The rule is short: a trailing comma is allowed when a closing bracket comes right after it.

[1, 2, 3,]       // ] closes the list
(a, b,) // ) closes it
<Key, Value,> // > closes it

The comma needs a closer to sit in front of. That bracket is what tells the compiler the list is finished, rather than half-typed.

That covers a lot:

// Tuples and tuple patterns
let coffee = (
beans: beans,
grind: .fine,
)

// Function and initializer parameters, and call arguments
makeCoffee(
beans: beans,
grind: .fine,
)

// Enum associated values
case brewed(
beans: Beans,
at: Date,
)

// Generic parameter and argument lists
struct Cache<
Key: Hashable,
Value,
> { }

// Closure capture lists
let closure = { [
weak self,
grinder,
] in
// ...
}

// Subscripts and key path subscripts
matrix[
row,
column,
]

// Macro and attribute arguments
@Model(
name: "Coffee",
version: 2,
)

String interpolation counts too, since \( and ) are a matched pair:

let label = "\(
name,
)"

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Where it does not

The lists left out are the ones with no closing bracket after them.

// Condition lists — the `{` is a block, not a closer
if
a,
b, // error
{ }

// Inheritance clauses
struct Coffee:
Codable,
Sendable, // error
{ }

// where clauses
extension Cache where
Key: Codable, // error
{ }

// Switch case labels and enum case declarations
case .espresso,
.ristretto, // error

In every one of these, the next thing is a { or a newline, not a closing bracket. So the compiler has no way to tell "I finished the list" from "I am about to write one more item" — the two look the same. Rather than guess, Swift rejects the comma.

The one-element rule

A trailing comma attaches to the last element, so there has to be a last element.

(1,)   // ok — a one-element list with a trailing comma
(,) // error — nothing to attach to

This matters mostly for code generators, which now don't need a special case for an empty list.

Why this exists

Two reasons, and the second is the more interesting one.

The obvious one is cleaner diffs, as above. Adding, removing, or reordering the last item in a list stops touching the line next to it, which means fewer spurious conflicts when two people edit the same parameter list.

The less obvious one is code generation. Macros build argument lists programmatically, and "emit a comma after every element except the last" is a small, permanent tax on every generator that produces Swift source. Now a generator can emit a comma after every element and stop thinking about it. Given how much Swift code macros now write, that adds up.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Summary

Swift 6.1 allows a trailing comma wherever a closing bracket follows it: parameters and arguments, tuples, enum associated values, generic parameter lists, closure capture lists, subscripts, attribute and macro arguments, and string interpolations.

It is not allowed in condition lists, inheritance clauses, where clauses, or case labels, because nothing closes those — so a trailing comma would be indistinguishable from an unfinished list.

It is a small feature, but it is the kind that quietly improves every code review you do afterwards.


Read more article about Swift, Swift Evolution, or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
How to use if and switch as expressions in Swift

Swift 5.9 lets an if or switch produce a value instead of just assigning one. It removes a var we never wanted, but the single-expression rule catches people out.

← Home