This one's very good point and it does make perfect sense. What actually got me thinking about this is that I find it a bit inelegant that Swift conflated a concept of classes and reference types. When you do need to have shared state that can be perfectly expressed as let's say an enum
or a struct
, you're still inclined to use classes either directly or as wrappers. And classes have their own baggage, like inheritance (classes aren't final
by default), initializer rules (convenience
/required
rules etc). I understand this conflation was mostly done for first-class (no pun intended!) compatibility with Objective-C, but wouldn't it be more elegant if Swift had separate standard immutable and mutable reference types like Rust has with &
and &mut
?
A naive implementation could look like this:
final class ClassReference<T> {
var value: T
init(_ value: T) {
self.value = value
}
}
public struct Reference<T> {
fileprivate let reference: ClassReference<T>
public var value: T {
get {
return reference.value
}
}
public init(_ value: T) {
reference = ClassReference(value)
}
public init(_ mutable: MutableReference<T>) {
reference = mutable.reference
}
}
public struct MutableReference<T> {
fileprivate let reference: ClassReference<T>
public var value: T {
get {
return reference.value
}
set {
reference.value = newValue
}
}
public init(_ value: T) {
reference = ClassReference(value)
}
}
public struct WeakReference<T> {
private weak var reference: ClassReference<T>?
public var value: T? {
get {
return reference?.value
}
set {
guard let v = newValue else {
reference = nil
return
}
reference?.value = v
}
}
public init(_ strong: Reference<T>) {
self.reference = strong.reference
}
}
If Swift supported typealias operators, it could be quite handy to have something like this as well:
// pseudocode
typealias &T = Reference<T>
An obvious use case for it is shared state where you can explicitly make it immutable when needed, while still keeping it readable and up to date after it's been modified:
enum State {
case foo
case bar
}
let state = MutableReference(State.foo)
let result1 = stateModifier(state)
let result2 = stateReader(Reference(state)) // making it immutable here