- Proposal: SE-0003
- Author: David Farler
- Status: Accepted
- Review manager: Joe Pamer
This proposal underwent some major changes from its original form. See the end of this document for historical information and why this proposal changed.
There has been some confusion of semantics when a function parameter is
marked as inout
compared to var
. Both give a mutable local copy of a
value but parameters marked inout
are automatically written back.
Function parameters are immutable by default:
func foo(i: Int) {
i += 1 // illegal
}
func foo(var i: Int) {
i += 1 // OK, but the caller cannot observe this mutation.
}
Here, the local copy of x
mutates but the write does not propagate back to
the original value that was passed, so the caller can never observe the change
directly. For that to happen to value types, you have to mark the parameter
with inout
:
func doSomethingWithVar(var i: Int) {
i = 2 // This will NOT have an effect on the caller's Int that was passed, but i can be modified locally
}
func doSomethingWithInout(inout i: Int) {
i = 2 // This will have an effect on the caller's Int that was passed.
}
var x = 1
print(x) // 1
doSomethingWithVar(x)
print(x) // 1
doSomethingWithInout(&x)
print(x) // 2
Using var
annotations on function parameters have limited utility,
optimizing for a line of code at the cost of confusion with inout
,
which has the semantics most people expect. To emphasize the fact these
values are unique copies and don't have the write-back semantics of
inout
, we should not allow var
here.
In summary, the problems that motivate this change are:
var
is often confused withinout
in function parameters.var
is often confused to make value types have reference semantics.- Function parameters are not refutable patterns like in if-, while-, guard-, for-in-, and case statements.
This is a trivial change to the parser. In Swift 2.2, a deprecation warning will be emitted while in Swift 3 it will become an error.
As a purely mechanical migration away from these uses of var
, a temporary
variable can be immediately introduced that shadows the immutable copy in all of
the above uses. For example:
func foo(i: Int) {
var i = i
}
However, shadowing is not necessarily an ideal fix and may indicate an anti-pattern. We expect users of Swift to rethink some of their existing code where these are used but it is not strictly necessary to react to this language change.
This proposal originally included removal of var
bindings for all
refutable patterns as well as function parameters.
Removal of var
from refutable patterns was reconsidered due to the
burden it placed on valid mutation patterns already in use in Swift 2
code. You can view the discussion on the swift-evolution mailing list
here:
Initial Discussion of Reconsideration
The rationale for a final conclusion was also sent to the swift-evolution list, which you can view here: