Dear Erica: “Bah. Swift 2.whatever removed func _( var _ : _){}
. I found it a useful convenience. How do I work around this?” — Rather Outspoken
Starting in Swift 2.2, SE-0003 removed var
parameters from the language. Both var
and inout
created mutable parameters but only those labeled inout
wrote values back to the originating instance.
This language evolution really only affects value types. Reference types already allow modification because the reference doesn’t change. It’s only the data that the reference points to. This is still legal in Swift 2.2:
func modifyMyLabel(myLabel: UILabel) { myLabel.text = "Hello" } let label = UILabel() modifyMyLabel(label) print(label.text) // Optional("Hello")
Imagine in pre-Swift 2.2 you had this code:
func foo(var i: Int) { i += 1 // the change to i would not propagate back }
Now, if you want to modify a value passed as an argument without using inout
, you must create a var
instance within the function scope, the most common way to do this is shadowing, or by declaring a new variable with the same name as the one used in the function signature:
func foo(i: Int) { // no "var" allowed var i = i // shadowing i += 1 // the change to i still doesn't propagate back }
Shadowing fixes most problems with the language update. In more complex situations, Brent Royal Gordon and I came up with a slightly different solution. We have a proposal in the Swift Evolution pull request queue that introduces a with
operator to handle updates to immutable constants. To give an example of use, the standard library includes an operator for concatenating two RangeReplaceableCollection
s with this implementation:
var lhs = lhs
// FIXME: what if lhs is a reference type? This will mutate it.
lhs.reserveCapacity(lhs.count + numericCast(rhs.count))
lhs.append(contentsOf: rhs)
return lhs
Using our proposed with
function, you can eliminate the shadowing of lhs
:
// FIXME: what if lhs is a reference type? This will mutate it.
return with(lhs) {
$0.reserveCapacity($0.count + numericCast(rhs.count))
$0.append(contentsOf: rhs)
}
Our proposed with
implementation does not resolve the “FIXME” comment. Like the var lhs = lhs
in the original code, with
only copies value types, not reference types. If RangeReplaceableCollection
included a Foundation-like copy()
method that was guaranteed to return a copy even if it was a reference type, with
would work nicely with that solution:
return with(lhs.copy()) {
$0.reserveCapacity($0.count + numericCast(rhs.count))
$0.append(contentsOf: rhs)
}
Comments are closed.