-3

Running the below piece of code on Xcode 9 playground, I noticed that nil does not equal nil. How can we check if b is in fact nil at line #4?

let s: String? = nil
var a: Optional<Any> = .some(s as Any)
if let b = a {
    if b == nil {
        print("b equals nil")
    } else {
        print("b doesn't equal nil. b is \(b)")
    }
}

enter image description here

UPDATE 1:

I do understand why the behavior is so. What I am looking for is how to check if b is nil since comparing it with nil doesn't work here.

UPDATE 2:

To avoid confusion, I changed the var name to b at line 3 (if let b = a)

UPDATE 3:

The answer turns out to be like this:

let s: String? = nil
var a: Optional<Any> = .some(s as Any)
if let b = a {
    if Mirror(reflecting: b).descendant("Some") as? String == nil {
        print("b equals nil")
    } else {
        print("b doesn't equal nil. b is \(b)")
    }
}
Thanh Pham
  • 2,021
  • 21
  • 30

4 Answers4

2

When you write:

let s: String? = nil

You basically create a generic enum value: s: Optional<String> = .none

enum Optional<T> {
    case .some(T)
    case .none
}

Then you wrap that value into a new enum as associated value for .some :

var a: Optional<Any> = .some(s as Any)

which is the same as typing:

var a: Optional<Any> = .some(Optional<String>.none as Any)

so a by itself is not nil. It contains .some value wrapped. When you unwrap it, you still get nested wrapped Optional. This is why you pass further than line #4. But inherent value of Optional is still nil. This is why you see it printed.

Hexfire
  • 5,945
  • 8
  • 32
  • 42
  • 1
    this should be the correct answer. as it explains that 'a' by itself is not nil but a generic Optional object with a 'Wrapped' value in it, thats why the if let statement succeeds. – Rishabh Oct 26 '17 at 05:54
  • what is the result if you assign as `let a:Any? = s as Any` – Jaydeep Vyas Oct 26 '17 at 06:00
  • IMHO the _real_ question here is why the comparison with `nil` in `a == nil` fails, and that’s because of the `Any` type. That’s what’s surprising about the code – simply nesting a `nil` inside another optional (`.some(Optional.none)`) would work as expected, printing `a equals nil`. – zoul Oct 26 '17 at 06:07
  • @zoul This is not the case, because here `a` is not `nil` in `a == nil` comparison. It's an enum value with the wrapped optional, which, in turn, is nil. – Hexfire Oct 26 '17 at 06:10
  • Try using `dump` just before the comparison, the outer optional was stripped by the `if let` assignment and the inner optional is `nil`. – zoul Oct 26 '17 at 06:12
1

As the warning states:

Comparing non-optional of type Any to nil always returns false. Here the if let statement make 'a' variable a non-optional type and hence you always get false.

As for answering the original question, you have done it the right way thats how you can check for a nil value:

if a == nil {}

or like this

if (!a) {}

For more on how swift represents optional values check the answer in this question-

Why non optional Any can hold nil?

Rishabh
  • 465
  • 5
  • 14
0

I don't know why you are creating another variable a.

But I tried your code like this and it worked:

let s: String? = nil
let a: Any? = s
//if let a = a {
    if a == nil {
        print("a equals nil")
    } else {
        print("a doesn't equal nil. a is \(String(describing: a))")
    }
//}
Amit
  • 4,837
  • 5
  • 31
  • 46
0

Nullable types present in swift as (<YourTypy>, nil). For example, String? presents as (String, nil). So, a type of myVar: String? can be equal String or nil.

When you use if let a = a structure, it moves into true branch only if type of a equals YourType, not nil. And in true branch you will have variable a with type YourType, not YourType?. So, a in true branch will never equal nil.

You should write notification about nil in false branch of if let

Axazeano
  • 890
  • 9
  • 23