Skip to content

Commit 1bb204e

Browse files
authored
Merge pull request #3342 from Azoy/se532-revisions
[SE-0532] Revise SE-0532 for properties and put
2 parents 49216cb + b310b3b commit 1bb204e

1 file changed

Lines changed: 39 additions & 38 deletions

File tree

proposals/0532-optional-noncopyable-improvements.md

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
* Proposal: [SE-0532](0532-optional-noncopyable-improvements.md)
44
* Author: [Alejandro Alonso](https://github.com/Azoy)
55
* Review Manager: [John McCall](https://github.com/rjmccall)
6-
* Status: **Active review (May 26th...June 8th, 2026)**
6+
* Status: **Active review (May 26th...July 6th, 2026)**
77
* Implementation: [swiftlang/swift#88505](https://github.com/swiftlang/swift/pull/88505)
8+
* Previous revision: [1](https://github.com/swiftlang/swift-evolution/blob/49216cb8b9063e6031eca47033067f3224386b57/proposals/0532-optional-noncopyable-improvements.md)
89
* Review: ([pitch](https://forums.swift.org/t/pitch-optional-noncopyable-improvements-and-generalizations/86656)) ([review](https://forums.swift.org/t/se-0532-optional-noncopyable-improvements-and-generalizations/86941))
910

1011
## Summary of changes
1112

12-
Introduces three new methods on `Optional` to help support noncopyable wrapped
13-
values: `borrow()`, `mutate()`, and `insert()`. Also generalizes `map`,
14-
`flatMap`, and `unsafelyUnwrapped`.
13+
Introduces two new properties and a new method on `Optional` to help support
14+
noncopyable wrapped values: `ref`, `mutableRef`, and `put()`. Also generalizes
15+
`map`, `flatMap`, and `unsafelyUnwrapped`.
1516

1617
## Motivation
1718

@@ -92,7 +93,7 @@ noncopyable wrapped values being more verbose than its predecessor.
9293

9394
## Proposed solution
9495

95-
We introduce two new methods on `Optional`: `borrow()` and `mutate()`. These will
96+
We introduce two new properties on `Optional`: `ref` and `mutableRef`. These will
9697
return references to the inner wrapped payload if there is one and `nil`
9798
otherwise.
9899

@@ -101,7 +102,7 @@ func bar(_ x: borrowing SomeNoncopyable) {
101102
...
102103
}
103104

104-
if let payload = optional.borrow() {
105+
if let payload = optional.ref {
105106
bar(payload.value) // 'bar' gets passed a 'borrowing Wrapped'
106107
}
107108

@@ -113,7 +114,7 @@ func baz(_ x: inout SomeNoncopyable) {
113114
...
114115
}
115116

116-
if var payload = optional.mutate() {
117+
if var payload = optional.mutableRef {
117118
baz(&payload.value) // 'baz' can mutate the payload in place!
118119
}
119120

@@ -148,7 +149,7 @@ optMutex?.withLock { ... } // error: use of 'optMutex' after consume
148149

149150
------
150151

151-
A quality of life API we're also proposing is `Optional.insert`. Consider the
152+
A quality of life API we're also proposing is `Optional.put()`. Consider the
152153
following pattern:
153154

154155
```swift
@@ -174,7 +175,7 @@ cache.opt!.append(newItem)
174175
The use of `!` here is really unnecessary because we already know there's a value
175176
in the optional. In some cases, this `!` may not get optimized away if
176177
you're calling into a non-inlined function taking `Cache` because it can't make
177-
any assumptions about the values that exist in it. We can safely model an `insert`
178+
any assumptions about the values that exist in it. We can safely model a `put()`
178179
method that returns a direct mutable reference to the payload of an optional
179180
given a new item to put into the optional:
180181

@@ -188,7 +189,7 @@ var cache = Cache()
188189
// do some computation
189190

190191
let items: UniqueArray<Int> = fooBar()
191-
var itemsRef = cache.opt.insert(items)
192+
var itemsRef = cache.opt.put(items)
192193

193194
// more calculations, maybe some
194195
// API calls
@@ -199,36 +200,40 @@ itemsRef.value.append(newItem)
199200

200201
## Detailed design
201202

202-
### `borrow()` and `mutate()`
203+
### `ref` and `mutableRef`
203204

204205
```swift
205206
extension Optional where Wrapped: ~Copyable & ~Escapable {
206207
/// Returns a borrowed reference to the payload within the optional, if there
207208
/// is one.
208-
@lifetime(borrow self)
209-
public func borrow() -> Ref<Wrapped>?
209+
public var ref: Ref<Wrapped>? {
210+
@lifetime(borrow self)
211+
get
212+
}
210213
}
211214

212215
extension Optional where Wrapped: ~Copyable {
213216
/// Returns the mutable reference to the payload within the optional, if there
214217
/// is one.
215-
@lifetime(&self)
216-
public mutating func mutate() -> MutableRef<Wrapped>?
218+
public var mutableRef: MutableRef<Wrapped>? {
219+
@lifetime(&self)
220+
mutating get
221+
}
217222
}
218223
```
219224

220-
Conceptually, these methods effectively move the ownership of the optional and
221-
its payload. In order to call `borrow()`, for example, you must have at least
225+
Conceptually, these properties effectively move the ownership of the optional and
226+
its payload. In order to call `ref`, for example, you must have at least
222227
a `borrowing Optional<T>` which, if you squint hard enough, is `Ref<Optional<T>>`.
223-
`borrow()` takes this `Ref<Optional<T>>` and produces `Optional<Ref<T>>` which
228+
`ref` takes this `Ref<Optional<T>>` and produces `Optional<Ref<T>>` which
224229
moved the reference inside the optional meaning we get back an owned value we
225230
can mutate, consume, or simply have exclusive access to.
226231

227-
The same is true for `mutate()`. It takes at least `inout Optional<T>` to be able to
232+
The same is true for `mutableRef`. It takes at least `inout Optional<T>` to be able to
228233
perform the call which can look like `MutableRef<Optional<T>>` into an owned
229234
`Optional<MutableRef<T>>` value.
230235

231-
### `insert()`
236+
### `put()`
232237

233238
```swift
234239
extension Optional where Wrapped: ~Copyable {
@@ -241,7 +246,7 @@ extension Optional where Wrapped: ~Copyable {
241246
/// - Returns: A mutable reference inside the optional to its newly inserted
242247
/// payload.
243248
@lifetime(&self)
244-
public mutating func insert(_ new: consuming Wrapped) -> MutableRef<Wrapped>
249+
public mutating func put(_ new: consuming Wrapped) -> MutableRef<Wrapped>
245250
}
246251
```
247252

@@ -290,13 +295,13 @@ func foo(x: consuming Optional<UniqueArray<String>>) -> Optional<String> {
290295
}
291296

292297
func bar(x: borrowing Optional<Atomic<Int>>) -> Optional<Int> {
293-
x.borrow().map { // ok!
298+
x.ref.map { // ok!
294299
$0.value.load(ordering: .relaxed) &+ 1
295300
}
296301
}
297302

298303
func baz(x: inout Optional<UniqueArray<Int>>) -> Optional<Int> {
299-
x.mutate().map {
304+
x.mutableRef.map {
300305
// Update the array while mapping over it
301306
$0.value.append(123)
302307
return $0.value.count
@@ -306,7 +311,7 @@ func baz(x: inout Optional<UniqueArray<Int>>) -> Optional<Int> {
306311

307312
## Source compatibility
308313

309-
`Optional.borrow()`, `Optional.mutate()`, and `Optional.insert()` are new
314+
`Optional.ref`, `Optional.mutableRef`, and `Optional.put()` are new
310315
methods so they shouldn't introduce any source compatibility issues.
311316

312317
The proposed overloads of `map` and `flatMap` are less-specialized overloads of
@@ -328,7 +333,7 @@ don't come with any new ABI nor break any old ABI.
328333

329334
## Implications on adoption
330335

331-
The `Optional.borrow()`, `Optional.mutate()`, and `Optional.insert()`
336+
The `Optional.ref`, `Optional.mutableRef`, and `Optional.put()`
332337
methods will have the same availability as `Ref` and `MutableRef`. The rest of
333338
the generalizatons will be marked as always available.
334339

@@ -348,7 +353,7 @@ if borrow x = optional {
348353
foo(optional) // ok
349354
```
350355

351-
If we decided this was a better direction than the `borrow()` and `mutate()`
356+
If we decided this was a better direction than the `ref` and `mutableRef`
352357
story, then we'd need to rethink how we want to generalize `map`, `flatMap`, and
353358
`unsafelyUnwrapped` because they can no longer be always consuming. We can provide
354359
`consumingMap`, `borrowingMap`, `mutatingMap`, etc. which is one solution, but
@@ -369,7 +374,7 @@ stable OS copies the wrapped payload.
369374

370375
### Automatic dereferencing for `Ref` and `MutableRef`
371376

372-
In the proposed solution, the examples using `borrow()` and `mutate()` to do a
377+
In the proposed solution, the examples using `ref` and `mutableRef` to do a
373378
`map` needed to explicitly access the `.value` property on these reference
374379
types. This isn't quite as ergonomic as the existing `map` on `Optional` that
375380
let's you interact with the passed parameter as the type itself. We could give
@@ -378,7 +383,7 @@ dereference themselves when accessing member properties or methods on them:
378383

379384
```swift
380385
func bar(x: borrowing Optional<Atomic<Int>>) -> Optional<Int> {
381-
x.borrow().map { // ok!
386+
x.ref.map { // ok!
382387
// No more '.value.'
383388
$0.load(ordering: .relaxed) &+ 1
384389
}
@@ -395,18 +400,14 @@ which would be useful for `UniqueBox` as well.
395400
### Change the default ownership of Optional bindings
396401

397402
In the motivation for some of the methods of this proposal, it's stated that we
398-
need methods like `borrow()` and `mutate()` to allow for borrowing versions of
403+
need properties like `ref` and `mutableRef` to allow for borrowing versions of
399404
control flow. We could instead change the default ownership of these `if let`
400405
scenarios to be borrowing by default. However, this would be a source-breaking
401406
change for code that expects optional binding to be consuming.
402407

403-
### `Optional.ref` and `Optional.mutableRef` properties
404-
405-
Instead of the `borrow()` and `mutate()` method names, we could have properties
406-
that returned the same value like the various `.span` and `.mutableSpan`
407-
properties.
408+
### `Optional.borrow()` and `Optional.mutate()` methods
408409

409-
However, we feel that the nature of the verbs `borrow` and `mutate` fit quite
410-
well in API usage especially for things like `opt.borrow().map { ... }`. It also
411-
mimics the recently accepted [Borrow Accessors SE-0507](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0507-borrow-accessors.md)
412-
names.
410+
In a previous revision of this proposal, we suggesed the method names `borrow()`
411+
and `mutate()` instead of the `ref` and `mutableRef` properties. It was decided
412+
that the properties were more desired as they somewhat mimic the `span` and
413+
`mutableSpan` properties found on types like `InlineArray` and `UniqueArray`.

0 commit comments

Comments
 (0)