Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's wrong with comparing non-optional bool in a single if structure in swift

Tags:

I have been coding in Swift for a while, and I think I had to put a ! on all my let field variables that were not defined immediately.

Now I notice today that this piece of code does not compile and I am really surprised? why is this?

class MyClass : Mapper {
    var a: Bool!

    required init?(_ map: Map) {
    }

    // Mappable
    func mapping(map: Map) {
        a   <- map["a"]
    }
}

let myClass = MyClass()

if myClass.a { // Compiler not happy
    //  Optional type 'Bool!' cannot be used as a boolean; test for '!= nil' instead
}

if true && myClass.a { // Compiler happy

}

if myClass.a && myClass.a { // Compiler happy

}

Apple Swift version 2.2

Edit
Some people point out why am I using a let for a variable that will never change. I mentioned it is for field variables but I shorten the example. When using ObjectMapper (http://github.com/Hearst-DD/ObjectMapper), all the fields are not defined immediately in the init. This is why they are all either optional? or required!

like image 495
jsgoupil Avatar asked May 06 '16 16:05

jsgoupil


2 Answers

You can declare let a: Bool without a ! and without declaring true or false right then. The compiler will complain if it can't guarantee the value gets set before you use it though.

This works.

let a: Bool

a = true

if a { // Compiler happy

}

This works as well, because a is guaranteed to be set.

let a: Bool

if thingOne < thingTwo {
    a = true
} else {
    a = false
}

if a { // Compiler happy

}

However, this would not work because a is not guaranteed to be set before you try to use it.

let a: Bool

if thingOne < thingTwo {
    a = true
}

if a { // Compiler NOT happy
    // "Constant 'a' used before being initialized"
}

Now, if you can't guarantee that your variable will be set by the time you do the check, then you should really be using an optional var in the first place.

var a: Bool?

if a == true { // Compiler happy

}
like image 87
Mike Cole Avatar answered Sep 29 '22 07:09

Mike Cole


A bit of history...

In Swift 1.0, it was possible to check if an optional variable optVar contained a value by just checking:

if optVar {
    println("optVar has a value")
} else {
    println("optVar is nil")
}

In The Swift Programming Language, the update for Swift 1.1 (dated 2014-10-16) stated:

Optionals no longer implicitly evaluate to true when they have a value and false when they do not, to avoid confusion when working with optional Bool values. Instead, make an explicit check against nil with the == or != operators to find out if an optional contains a value.

So, the nonsensical error message that you are getting was put there because the Swift compiler is interpreting your:

if a {
}

to mean:

if a != nil {
}

and it is encouraging you to test against nil to determine if the Optional a has a value.

Perhaps the Swift authors will change it in the future, but for now you will have to explicitly unwrap a:

if a! {
}

or check against true:

if a == true {
}

or (to be completely safe):

if a ?? false {
    print("this will not crash if a is nil")
}
like image 29
vacawama Avatar answered Sep 29 '22 05:09

vacawama