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)[email protected]:1:5 specialized=no)))
(brace_stmt))))
if let y: Int? = nil { }
completely misses the point of optional binding, so I don't see how this is a problem you're actually running into. – Sturgillvar x: Int? = nil; if let y: Int? = x { ... }
works. It's the key that unlocks both those other questions. Otherwise none of it make much sense (for me anyway). I also thought it would be the right place for @rintaro to explain where his answer came from. – Nympha