Jeffery Thomas Jeffery Thomas - 5 months ago 24
Swift Question

What's the difference between if nil != optional … and if let _ = optional …

I need to test if an expression which returns an optional is

nil
. This seems like a no-brainer, but here is the code.

if nil != self?.checklists.itemPassingTest({ $0 === note.object }) {

}


Which, for some reason, looks unpleasant to my eye.

if let item = self?.checklists.itemPassingTest({ $0 === note.object }) {

}


Looks much better to me, but I don't actually need the item, I just need to know if one was returned. So, I used the following.

if let _ = self?.checklists.itemPassingTest({ $0 === note.object }) {

}


Am I missing something subtle here? I think
if nil != optional …
and
if let _ = optional …
are equivalent here.




Update to address some concerns in the answers


  1. I don't see the difference between
    nil != var
    and
    var != nil
    , although I generally use
    var != nil
    . In this case, pushing the
    != nil
    after the block gets the boolean compare of block mixed in with the boolean compare of the if.

  2. The use of the Wildcard Pattern should not be all that surprising or uncommon. They are used in tuples
    (x, _) = (10, 20)
    , for-in loops
    for _ in 1...5
    , case statements
    case (_, 0):
    , and more (NOTE: these examples were taken from The Swift Programming Language).



This question is about the functional equivalency of the two forms, not about coding style choices. That conversation can be had on programmers.stackexchange.com.




After all this time, Swift 2.0 makes it moot

if self?.checklists.contains({ $0 === note.object }) ?? false {

}

Answer

After optimization, the two approaches are probably the same.

For example, in this case, compiling both the following with swiftc -O -emit-assembly if_let.swift:

import Darwin

// using arc4random ensures -O doesn’t just
// ignore your if statement completely
let i: Int? = arc4random()%2 == 0 ? 2 : nil

if i != nil {
  println("set!")
}

vs

import Darwin

let i: Int? = arc4random()%2 == 0 ? 2 : nil

if let _ = i {
  println("set!")
}

produces identical assembly code:

    ; call to arc4random
    callq   _arc4random
    ; check if LSB == 1 
    testb   $1, %al
    ; if it is, skip the println
    je  LBB0_1
    movq    $0, __Tv6if_let1iGSqSi_(%rip)
    movb    $1, __Tv6if_let1iGSqSi_+8(%rip)
    jmp LBB0_3
LBB0_1:
    movq    $2, __Tv6if_let1iGSqSi_(%rip)
    movb    $0, __Tv6if_let1iGSqSi_+8(%rip)
    leaq    L___unnamed_1(%rip), %rax  ; address of "set!" literal
    movq    %rax, -40(%rbp)
    movq    $4, -32(%rbp)
    movq    $0, -24(%rbp)
    movq    __TMdSS@GOTPCREL(%rip), %rsi
    addq    $8, %rsi
    leaq    -40(%rbp), %rdi
    ; call println
    callq   __TFSs7printlnU__FQ_T_
LBB0_3:
    xorl    %eax, %eax
    addq    $32, %rsp
    popq    %rbx
    popq    %r14
    popq    %rbp
    retq
Comments