Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save saagarjha/f33fecd4576f40133b6469da942ef453 to your computer and use it in GitHub Desktop.
Save saagarjha/f33fecd4576f40133b6469da942ef453 to your computer and use it in GitHub Desktop.

Remove implicitly unwrapped optionals in function signatures

  • Proposal: SE-NNNN
  • Author: spromicky
  • Status: Awaiting review
  • Review manager: TBD

Introduction

Swift, in contrast with Objective-C, makes a distinction between values that may be nil and values that can never be nil through its use of Optionals. Due to the fact that Objective-C does not make this distinction, Objective-C functions that do not use the Nullability annotations are imported with parameters of the implicitly unwrapped optional type. Unfortunately, this allows users to write their own Swift code that looks like this:

func foo(bar: Int!) {
	//…
}

Due to the confusion this may cause, we would like to propose the removal of implicitly unwrapped optionals in function signatures, both as parameters and as return values. Discussion on this topic may be found here.

Motivation

Implicitly unwrapped optionals are currently allowed in function declarations. Consider the following function:

func triple(forceUnwrapping aNumber: Int) -> Int {
	return aNumber * 3
}

let possiblyNil = Int("foo")
triple(forceUnwrapping: possiblyNil)

possiblyNil is an Int?; thus, this example will not compile due to triple(forceUnwrapping:) expecting an Int. It is easy to imagine a Swift beginner writing code that looks like this to "fix" the problem:

func triple(forceUnwrapping aNumber: Int!) -> Int {
	return aNumber * 3
}

let possiblyNil = Int("foo")
triple(forceUnwrapping: possiblyNil)

While this version compiles, it crashes due to the force unwrapping of a nil value. Unfortunately, the compiler "hides" this fact by making it seem like it's acceptable to pass in nil–it doesn't make the forced unwrapping explicit.

A second, and deeper, issue is the fact that this sort of usage is an abuse of implicitly unwrapped optionals. From The Swift Programming Language:

Sometimes it is clear from a program’s structure that an optional will always have a value, after that value is first set. In these cases, it is useful to remove the need to check and unwrap the Optional’s value every time it is accessed, because it can be safely assumed to have a value all of the time.

These kinds of optionals are defined as implicitly unwrapped optionals…

…Implicitly unwrapped optionals are useful when an Optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter. The primary use of implicitly unwrapped optionals in Swift is during class initialization…

As such, it can be said that an implicitly unwrapped optionals are a sort of agreement that while it may not have a value at initialization, by the time it is used it must have a valid value. If there is any doubt that a value will be nil during its lifetime, it should be declared an Optional, to make this uncertainty explicit. As parameters and return values can not enforce this "guarantee", they should be restricted to only allowing non-Optionals and Optionals, depending on whether the value can be nil.

Proposed solution

The safest solution, in this case, is to prevent the use of implicitly unwrapped optionals in function signatures. By forcing users to write

func triple(forceUnwrapping aNumber: Int) -> Int {
	return aNumber * 3
}

or

func triple(forceUnwrapping aNumber: Int?) -> Int {
	return aNumber * 3
}

the compiler will complain, reminding users that they should probably attempt to safely unwrap the Optional before using it, as well as making the implicitly unwrapped optional "contract" binding.

Detailed design

The proposal will prevent the use of implicitly unwrapped optionals in function signatures for both Swift code as well as imported Objective-C code. As non-annotated Objective-C functions are currently imported as implicitly unwrapped, they will be converted to Optionals as a preliminary step. Non-audited frameworks can be reviewed in the future so that they can be tagged with _Nonnull if necessary.

Impact on existing code

This proposal is a potentially source breaking change, but it should be easily mitigated using a migrator. Existing functions with implicitly unwrapped optionals can be changed to Optional; users can easily shadow variables with a guard or change their function to non-Optional. Objective-C frameworks without Nullability annotations will be imported as Optionals, again requiring that users handle nil properly (which they should be doing anyways, but if they aren't it serves as a good reminder).

Alternatives considered

Importing Objective-C functions as-is (that is, with implicitly unwrapped optionals), but disallowing implicitly unwrapped optionals in Swift code

This reduces the burden on existing frameworks and adding Nullability annotations, but creates a sort of disconnect between Objective-C and Swift in that it prevents Swift developers from writing functions signatures with implicitly unwrapped optionals.

Doing nothing

Obviously, this has the benefit of keeping the current behavior and not requiring a migrator or any source changes. However, I believe that the unsafe behavior that this encourages is not worth keeping.

@spromicky
Copy link

In my fork update author and link to mail discussion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment