I wonder if it's possible to modify some value of Optional without making a copy. The specific case where this comes up is conditionally creating a new value in Dictionary or appending to the current value.
This doesn't work because array gets copied:
switch dict[key] {
case .none:
dict[key] = [value]
case .some(var array):
array.append(value)
}
This works
if dict[key] == nil {
dict[key] = [value]
} else {
dict[key]!.append(value)
}
However the working version has ! in it which reviewers have to spend time on checking if it can fail. One could use ? instead but then it'd silently stop appending if there's a mistake (during refactoring) and would be a nightmare to debug.
In other words, I wonder if it's possible to to directly translate this Rust code:
match map.get_mut(&key) {
None => { map.insert(key, vec![value]); },
Some(vec) => vec.push(value),
}
Joe_Groff
(Joe Groff)
2
For this particular use case, you can use dictionary's default: subscript to provide the default value:
dict[key, default: []].append(value)
3 Likes
Thanks! Didn't notice this one in the docs.
lukasa
(Cory Benfield)
4
To answer the broader question: yes, you can do this.
extension Optional {
@discardableResult
mutating func modify<ReturnType>(_ closure: (inout Wrapped) throws -> ReturnType) rethrows -> ReturnType? {
if self == nil { return nil }
return try closure(&self!)
}
}
As with some other questions around the use of _modify, the admittedly opaque incantation &self! is carrying a lot of load here. This convinces Swift to elide a copy into a temporary when it passes the inner value into the function argument. I'm honestly not 100% that this works in all edge cases, but so far this has worked in all situations where I've needed to use it.
5 Likes
lukasa
(Cory Benfield)
5
As to other options with Dictionary specifically, there's also the answer I gave in this thread: Assigning a dictionary without re-hashing - #9 by lukasa