Add `modify…(…)` methods to `Dictionary` / `MutableCollection` / `Optional`

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 ... }
6 Likes

Thanks for chiming in, @Joe_Groff & @John_McCall! Much appreciated.