Reputation: 30458
Okay, so I know the normal way to use optionals in Swift is via optional bindings to unwrap them, like so...
let stringA:String? = nil // (or "ABC")
if let unwrappedStringA = stringA
{
let x:String = unwrappedStringA
}
But I've also seen the following way using implicitly-unwrapped optionals, which I personally think looks cleaner as it not only doesn't require an extra variable, but it also 'reads' better and more like English (i.e. 'If this is not nil, then...') and readability is one of the core tenets of Swift (especially in the upcoming Swift 3.0).
let stringA:String! = nil // (or "ABC")
if stringA != nil
{
let x:String = stringA
}
However, in regards to the latter, Swift 'purists' refer to this as 'code smell' and insist it's 'Bad, bad, bad!!'... but they never explain why! So... why is it so bad?
Note: Yes, I know about Optional chaining and other such features which you can't use with implicitly-unwrapped optionals, and they are all really cool features, but I am specifically asking about the technical downside to testing implicitly-unwrapped optionals against nil vs optional binding.
What I'm hoping for are quantifiable, technical reasons why one is better than the other (i.e. compiler optimizations, better safety checking, performance, etc.) In other words, not just a 'Hey, because that's not the 'Swifty' way to do it!' Hope that makes sense.
I actually just found a way to address one of my concerns (at least in a superficial way) which is having to create a new variable to hold the unwrapped one. The trick here is since the unwrapped variable is actually in a different scope than the optional itself, you can use the exact same name.
Further still, inside the brackets themselves is yet another scope which can also have a variable named stringA. This means you can now have three 'stringA' variables (but doesn't mean you should!)...
Here's this crazy code showing this...
let stringA:String? = nil // (or "ABC") // First stringA variable
if let stringA = stringA
{
let x:String = stringA // <- This stringA is the unwrapped optional (the second stringA)
// Now we're just getting crazy (and smelly!)
let stringA = stringA + stringA // <- This is yet another stringA (third one!)
let y:String = stringA
}
Again, I am not condoning this!! I'm just showing some code that others may find interesting from an instructive perspective. I sure did!
Upvotes: 3
Views: 580
Reputation: 14824
There is indeed a simple reason, and it's one of the ones you listed: "better safety checking". Optional bindings keep the scope of the unwrap known to the compiler, whereas it's the programmer's responsibility to keep track of when you have or have not checked an implicitly unwrapped optional for nil
.
With optional bindings:
let stringA:String? = nil // (or "ABC")
if let unwrappedStringA = stringA
{
let x:String = unwrappedStringA
}
accidentallyAttemptingToUse(stringA) // Compiler error! I see my mistake.
With implicit unwrapping:
let stringA:String! = nil // (or "ABC")
if(stringA != nil)
{
let x:String = stringA
}
accidentallyAttemptingToUse(stringA) // Runtime crash I might find in testing. Yuck.
What are IUOs good for?
Why have implicitly unwrapped optionals at all, then? They exist for basically one purpose: properties that will definitely have a value, but not until slightly after init
, so they have to be optional. Often @IBOutlet
s fall into this category: you can trust them to have a value, and you don't want to be unwrapping them all the time, but they are not assigned at init
, so you must make them optional. That is what implicitly unwrapped optionals are for.
Unwrapping to a variable of the same name
Your update, which tentatively suggests unwrapping optionals into variables with the same name, is actually excellent Swift style, and has been used by Apple early and often. Code is easier to read when you unwrap to variables of the same name, because you can track fewer (and better) names. Definitely embrace this!
let stringA:String? = nil // (or "ABC")
if let stringA = stringA
{
let x:String = stringA // Of *course* this is still named `stringA`;
// it's representing the same concept, after all.
}
Upvotes: 8
Reputation: 154711
Due to a change to implicitly unwrapped optionals in Swift 3, you will be much happier if you properly unwrap regular optionals instead of using implicitly unwrapped optionals.
In Swift 3, if you assign a String!
to a new variable, that variable will have the type String?
. This means your implicitly unwrapped optional becomes a regular optional on assignment, and you now have to deal with unwrapping the new variable.
This code works in Swift 2.x:
let stringA: String! = "Hello"
if stringA != nil
{
let y = stringA
let z = y + " world!"
}
In Swift 3:
let stringA: String! = "Hello"
if stringA != nil
{
let y = stringA
let z = y + " world!" // ERROR: value of optional type 'String?' not unwrapped; did you mean to use '!' or '?'?
}
If instead you use a String?
and unwrap it, then the code works as expected in both Swift 2.x and Swift 3:
let stringA: String? = "Hello"
if let stringA = stringA
{
let y = stringA
let z = y + " world!"
}
See the official discussion of this change here: SE-0054: Abolish ImplicitlyUnwrappedOptional Type
The Motivation section of this document states [emphasis added mine]:
The ImplicitlyUnwrappedOptional ("IUO") type is a valuable tool for importing Objective-C APIs where the nullability of a parameter or return type is unspecified. It also represents a convenient mechanism for working through definite initialization problems in initializers. However, IUOs are a transitional technology; they represent an easy way to work around un-annotated APIs, or the lack of language features that could more elegantly handle certain patterns of code. As such, we would like to limit their usage moving forward, and introduce more specific language features to take their place. Except for a few specific scenarios, optionals are always the safer bet, and we’d like to encourage people to use them instead of IUOs.
This proposal seeks to limit the adoption of IUOs to places where they are actually required, and put the Swift language on the path to removing implicitly unwrapped optionals from the system entirely when other technologies render them unnecessary. It also completely abolishes any notion of IUOs below the type-checker level of the compiler, which will substantially simplify the compiler implementation.
This makes it clear that the language designers believe that you should use Implicitly Unwrapped Optionals as little as possible.
Upvotes: 5
Reputation: 535989
Let's be clear on the real purpose of IUOs. They originated purely as a device for communication with Objective-C. Any object coming from Objective-C might be nil
, in theory, so in the beginning every object coming from Objective-C was an Optional. Having every such Optional be a true Optional was too disruptive, so such every object was an implicitly unwrapped Optional.
Then, however, Apple started hand-tweaking the APIs to tell Swift whether an object coming from Objective-C could in fact be nil
. That hand-tweaking process is just about finished now (Swift 3). Thus, everything coming from Objective-C is now either a normal object or a normal Optional (or, in some cases, a normal object that you have to pass thru a try
to obtain).
This being so, there is actually no need at all for implicitly unwrapped Optionals. They are nothing but a lazy convenience for programmers at this point. And other linguistic devices have come along to make them unnecessary. For example, it is a pain to unwrap an Optional with if let
and be forced to add a level curly braces, but now we have guard let
so you can unwrap without adding a level of curly braces.
Apple has therefore started to tighten the screws, making IUOs less and less convenient. For example, as vacawama has rightly said, they no longer propagate by assignment. And there is no longer any such thing as an Array of IUOs (e.g., [Int!]
is no longer a type).
This process will continue until there are few if any contexts in which an IUO is legal. It is thus best to get out of the habit of using them now.
Upvotes: 3