Permalink
113 lines (81 sloc) 3.78 KB

Removing var from Function Parameters

Note

This proposal underwent some major changes from its original form. See the end of this document for historical information and why this proposal changed.

Introduction

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

Motivation

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 with inout 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.

Design

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.

Impact on existing code

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.

Alternatives considered

This proposal originally included removal of var bindings for all refutable patterns as well as function parameters.

Original SE-0003 Proposal

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:

Note on Revision of the Proposal