Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Swift, how do I avoid both optionals and nil object references?

The whole reason for optionals is to prevent runtime crashes resulting from hitting a variable assigned to nil/null/none. Therefore, variables can't be nil; instead they can be wrapped in an Optional type that represents them as Some or None, and unwrapped to get the specific contents of Some, or nil.

But if you unwrap them all over the place with !, or with Implicitly Unwrapped Optionals, you just introduce the possibility, since you're an imperfect coder, of runtime crashes. If you use if let to safely unwrap them, you avoid crashes, but you are stuck inside the scope of the if let statement to work with the value inside Some, and you still have to handle the potential nil case. If you use ? to unwrap temporarily for calling a method, it will be rewrapped when complete, introducing the messy possibility of multiply layered optional wrapping.

So: It appears to me that the thing to do is avoid optionals except when necessary (e.g. when calling framework methods that return them). But if I'm not using optionals, that means my object references need to be non-nil, and I can't figure out how to handle cases where, for one reason or another, there shouldn't be, or isn't, a value assigned to an object reference.

My question is: How do I avoid the need for nil? It seems like it requires a different programming approach. (Or am I supposed to just use optionals, and if that's what I'm doing, how is it any better than simply having null assignments to object references like other languages have?)

I know this is potentially a subjective question, but where else am I supposed to ask it? I'm not trying to troll or stir up debate, I'd really like to know what the correct approach is as I write more Swift code.

like image 977
Ivan X Avatar asked Dec 23 '14 15:12

Ivan X


People also ask

What is an optional in Swift and what problem do optionals solve?

Optionals are a fundamental part of swift coding. Keeping it simple, we can say that optionals help us to separate good code from bad code and hence prevent crashes. Different programming languages uses different preventive measures to avoid a crash. But most of them are helpless in most of the cases.

How do I stop optional in Swift?

Avoid Optional values by initializing the variable with a non-optional value. This way the variable will always have a value. Later you can update the variable with the intended value — for example, a username that is returned from an async network request.

What is the point of optionals in Swift?

Optionals are in the core of Swift and exist since the first version of Swift. An optional value allows us to write clean code with at the same time taking care of possible nil values. If you're new to Swift you might need to get used to the syntax of adding a question mark to properties.

Are optionals weak in Swift?

No, weak and optional are not the same, but there is some interplay between the two. Optional just means that a variable can be nil , either by assigning nil yourself, or becoming nil through some other means. The weak keyword has to do with memory management.


2 Answers

You’re right, optionals can be a pain, which is why you shouldn't overuse them. But they're more than just something you have to deal with when using frameworks. They're a solution to an incredibly common problem: how to handle a call that returns a result that might be OK, or might not.

For example, take the Array.first member. This is a handy utility that gives you the first element of an array. Why is it useful to be able to call a.first, when you could just call a[0]? Because at runtime the array might be empty, in which case a[0] will explode. Of course you can check a.count beforehand – but then again

a. you might forget,

and

b. that results in quite ugly code.

Array.first deals with this by returning an optional. So you are forced to unwrap the optional before you can use the value that is the first element of the array.

Now, to your issue about the unwrapped value only existing inside the block with if let. Imagine the parallel code, checking the array count. It would be the same, right?

if a.count > 0 {     // use a[0] } // outside the block, no guarantee // a[0] is valid  if let firstElement = a.first {     // use firstElement } // outside the block, you _can't_ // use firstElement 

Sure, you could do something like perform an early return from the function if the count is zero. This works but is a bit error-prone – what if you forgot to do it, or put it in a conditional statement that didn't happen to run? Essentially you can do the same with array.first: check the count early in the function, and then later on do array.first!. But that ! is like a signal to you – beware, you are doing something dangerous and you will be sorry if your code isn't completely correct.

Optionals also help make the alternatives slightly prettier. Suppose you wanted to default the value if the array was empty. Instead of this:

array.count > 0 ? a[0] : somedefault 

you can write this:

array.first ?? somedefault 

This is nicer in several ways. It puts the important thing up front: the value you want is how the expression starts, followed by the default. Unlike the ternary expression, which hits you first with the checking expression, followed by the value you actually want, then finally the default. It's also more foolproof – much easier to avoid making a typo, and impossible for that typo to result in a runtime explosion.

Take another example: the find function. This checks if a value is in a collection and returns the index of its position. But the value may not be present in the collection. Other languages might handle this by returning the end index (which doesn't point to a value but rather one past the last value). This is what's termed a "sentinel" value – a value that looks like a regular result, but actually has special meaning. Like the previous example, you’d have to check the result was not equal to the end index before using it. But you have to know to do this. You have to look up the docs for find and confirm that's how it works.

With find returning an optional it's just natural, when you understand the optional idiom, to realize that the reason it does is because the result might not be valid for the obvious reason. And all the same things about safety mentioned above also apply – you can’t accidentally forget, and use the result as an index, because you have to unwrap it first.

That said, you can over-use optionals as they are a burden to have to check. This is why array subscripts don't return optionals – it would create so much hassle to have to constantly check and unwrap them, especially when you know for a fact that the index you're using is valid (for example, you're in a for loop over a valid index range of the array) that people would be using ! constantly, and thus clutter their code without benefit. But then the helper methods like first and last are added to cover common cases where people do want to quickly do an operation without having to check the array size first, but want to do it safely.

(Swift Dictionaries, on the other hand, are expected to be regularly accessed via invalid subscripts, which is why their [key] method does return an optional)

Even better is if the possibility of failure can be avoided altogether. For example, when filter matches no elements, it doesn’t return a nil optional. It returns an empty array. “Well obviously it would”, you might say. But you’d be surprised how often you see someone making a return value like an array optional when actually they just ought to return an empty one. So you’re completely correct in saying you should avoid optionals except when they’re necessary – it’s just a question of what necessary means. In the examples above I’d say they’re necessary, and a better solution to the alternatives.

like image 178
Airspeed Velocity Avatar answered Sep 21 '22 01:09

Airspeed Velocity


Or am I supposed to just use optionals, and if that's what I'm doing, how is it any better than simply having null assignments to object references like other languages have?

If your computation may need to return an “special” value, then yes, in Swift you are supposed to use optionals. They are better than nullable types because they are explicit. It’s easy to miss a case where a pointer might be nil, it’s much harder (but entirely possible) to screw up with optionals.

If you use “if let” to safely unwrap them, you avoid crashes, but you are stuck inside the scope of the “if let” statement to work with the value, and you still have to handle the potential nil case.

That’s a feature. I mean, that’s the whole point of an optional type: you have to handle both cases (nil and non-nil) and you have to be explicit about it.

See Haskell’s Maybe type and monad for an example of a similar concept. The Maybe type is the exact equivalent of optional types, the Maybe monad makes it very easy to “chain” operations with these optional values without having to manually check for empty values all the time.

like image 29
zoul Avatar answered Sep 19 '22 01:09

zoul