What does Swift's optional binding do to the type it's arguments?

2 Solutions Collect From Internet About “What does Swift's optional binding do to the type it's arguments?”

OK, I will answer, with my poor English skills 😉

Let’s start with this:

if let lvalue:T = rvalue { ... }

At first the compiler tries to convert rvalue to T? by wrapping with Optional. For example:

typealias T = Int
let rvalue:Int? = 1
if let lvalue:T = rvalue { ... } // do nothing because `rvalue` is already `T?`

//---

typealias T = Int??
let rvalue:Int = 1
if let lvalue:T = rvalue { ... } // rvalue will be converted to `T?`, that is `Int???`

//---

typealias T = Int
let rvalue:Int?? = 1
if let lvalue:T = rvalue { ... } // error because `rvalue` could not be converted by wrapping with Optional

Then the runtime look into converted rvalue by unwrapping once whether that value is nil or not. If not nil then assign and success.

This is the rule for if let lvalue:T = rvalue { ... }


On the other hand,

let lvalue:T = rvalue

It’s similar but not the same. The compiler tries to convert rvalue to T, not T?.

typealias T = Int??
let rvalue:Int?? = 1
let lvalue:T = rvalue // Do nothing because `rvalue` is `T`

//---

typealias T = Int??
let rvalue:Int = 1
let lvalue:T = rvalue // rvalue will be converted to `T`, that is `Int??`

Then the runtime can unconditionally assign rvalue to lvalue.

I think this is the difference.


You you want to observe these compiler works, you can use swiftc -dump-ast command.

$ cat test.swift
let i:Int? = 1
if let y:Int? = i { }

$ xcrun swiftc -dump-ast test.swift
(source_file
  (top_level_code_decl
    (brace_stmt
      (pattern_binding_decl
        (pattern_typed type='Int?'
          (pattern_named type='Int?' 'i')
)
        (inject_into_optional implicit type='Int?' location=test.swift:1:14 range=[test.swift:1:14 - line:1:14]
          (call_expr implicit type='Int' location=test.swift:1:14 range=[test.swift:1:14 - line:1:14]
            (constructor_ref_call_expr implicit type='(_builtinIntegerLiteral: Int2048) -> Int' location=test.swift:1:14 range=[test.swift:1:14 - line:1:14]
              (declref_expr implicit type='Int.Type -> (_builtinIntegerLiteral: Int2048) -> Int' location=test.swift:1:14 range=[test.swift:1:14 - line:1:14] decl=Swift.(file).Int.init(_builtinIntegerLiteral:) specialized=no)
              (type_expr implicit type='Int.Type' location=test.swift:1:14 range=[test.swift:1:14 - line:1:14] typerepr='<<IMPLICIT>>'))
            (tuple_expr implicit type='(_builtinIntegerLiteral: Int2048)' location=test.swift:1:14 range=[test.swift:1:14 - line:1:14] names=_builtinIntegerLiteral
              (integer_literal_expr type='Int2048' location=test.swift:1:14 range=[test.swift:1:14 - line:1:14] value=1)))))
)
  (var_decl "i" type='Int?' access=internal let storage_kind='stored')
  (top_level_code_decl
    (brace_stmt
      (if_stmt
        (pattern_binding_decl
          (pattern_typed type='Int?'
            (pattern_named type='Int?' 'y')
)
          (inject_into_optional implicit type='Int??' location=test.swift:2:17 range=[test.swift:2:17 - line:2:17]
            (declref_expr type='Int?' location=test.swift:2:17 range=[test.swift:2:17 - line:2:17] decl=test.(file).i@test.swift:1:5 specialized=no)))

        (brace_stmt))))

Think about what optional binding is for. It allows you to take an optional value, and condition on whether it is nil or not, and if it is not nil, to unwrap the contained value and bind it to a variable. So it is like this:

if let non_optional_var = optional_expr {
    ...
} else {
    ...
}

Thus if optional_expr has type T?, then non_optional_var has type T. (When I wrote “non_optional_var” I didn’t mean literally that it can’t be optional, but to express that it has one less level of optional-ness than the “optional_expr”. Thus, if non_optional_var is type Int?, then optional_expr has type Int??.

By the way, the optional binding syntax is syntactic sugar for switching on the Optional enum:

switch optional_expr {
case .Some(let non_optional_var):
    ...
case .None:
    ...
}