Expanding that to a matched pair of counterproposal sketches:
Nonescapable Reference Types
- Add
Reference<T: ~Copyable>: ~Escapable {}
andMutableReference<T: ~Copyable>: ~Escapable {}
types to the language. They have no API surface; you always interact with them via the language's usual syntax. MutableReference<T>
is a subtype ofReference<T>
and upcasts silently[Mutable]Reference<T>
is a subtype of[Mutable]Reference<U>
ifT
is a subtype ofU
, upcasts silently, and downcasts withas?
- probably need to allow projecting stored properties out of references?
&lvalue
syntax createsReference
/MutableReference
as appropriate.- If
T
isCopyable
- A value of type
Reference<T>
can be used wherever a value of typeT
can (copied out if necessary). - A value of type
MutableReference<T>
, can be used wherever an lvalue of typeT
can.
- A value of type
borrowing T
parameters become syntax sugar for regular parameters of typeReference<T>
- (we gain additional new functionality because now we have mutable borrowing parameters too)
Reference Accessors
(deliberately picking new keywords here to encourage discussing this without trampling over the previous mentions of borrow
accessors, though I expect we'd choose borrow
if this proposal were actually adopted)
- Add
reference
andmutableReference
accessors.var t: T { reference { ... } }
returnsReference<T>
var t: T { mutableReference { ... } }
returnsMutableReference<T>
.- providing
reference
synthesizesget
forCopyable
types - providing
mutableReference
synthesizesget
andset
forCopyable
types - read-only stored properties provide
get
andreference
- mutable stored properties provide
get
,reference
,set
andmutableReference
reference set
is a legal combination
dict[key] // borrows value, returning Reference<Value>?, all good
dict[key] = nil // calls `set` with `Value?`, works fine
dict[key]?.append(3) // works for copyable types, but not for noncopyables
// because we have to copy out of Reference<Value> to be able to call
// mutating append.
// but, we can now
// assuming func slot(key: Key) -> MutableReference<Value>?
dict.slot(key)?.append(3) // in-place update of the value
// or if `reference mutableReference set` were allowable,
// with the compiler picking contextually between
// `mutableReference` and `set`
// I think we could make both work:
dict[key] = nil // calls `set` 'cos we're replacing the whole value
dict[key]?.append(3) // calls `mutableReference` 'cos we don't need the whole value
These proposals don't address the "damage to existing swift code" criticism; it effectively splits the ecosystem. A protocol using { reference mutableReference }
for efficiency can't interoperate with a type with get set
. On the other hand, it does encourage people to stick with get set
unless they really need the efficiency or have noncopyables...