Language support for inout bindings would definitely be more ergonomic, but while we wait for that, you can also write a function to give yourself a scoped inout binding:
func update<T, R>(_ value: inout T, _ body: (inout T) -> R) -> R {
body(&value)
}
And an extension on Optional
to simulate if inout
:
extension Optional {
mutating func ifInout<R>(_ body: (inout Wrapped) -> R) -> R? {
switch self {
case .none: return .none
case .some: return .some(withUnsafeMutableBytes(of: &self) {
// Optional<Wrapped> with a value always stores Wrapped at the base address
body(&$0.baseAddress.unsafelyUnwrapped.assumingMemoryBound(to: Wrapped.self).pointee)
})
}
}
which gives you:
valueOrNil.ifInout { modifiedValue in ... }
dictionary[key].ifInout { modifiedValue in ... }
update(&dictionary[key, default: ...]) { modifiedValue in ... }
update(&array[index]) { modifiedElement in ... }