Dear Erica: ex-var parameters

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 RangeReplaceableCollections 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.