Swift的可选绑定对它的参数类型做了什么?

时间:2014-10-28 18:23:30

标签: if-statement swift syntax assignment-operator optional

为什么

if let y: Int? = nil { ... } 

the same as

if let y: Int? = nil as Int?? { ... }

(因而无效的分配),特别是在自己的

let y: Int? = nil

不同
let y: Int? = nil as Int??

(因为let y: Int? = nil是有效的作业)?

2 个答案:

答案 0 :(得分:1)

好的,我会用我糟糕的英语技能回答; - )

让我们从这开头:

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

首先,编译器尝试使用rvalue换行,将T?转换为Optional。例如:

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

然后,运行时通过展开一次来查看转换 rvalue,无论该值是否为nil。如果不是nil则分配并成功。

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

的规则

另一方面,

let lvalue:T = rvalue

它相似但不一样。编译器尝试将rvalue转换为T,而不是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??`

然后,运行时可以无条件地将rvalue分配给lvalue

我认为这是不同的。


您希望观察这些编译器的工作原理,您可以使用swiftc -dump-ast命令。

$ 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))))

答案 1 :(得分:1)

考虑可选绑定的用途。它允许您获取一个可选值,并调整是否为nil,如果它不是nil,则打开包含的值并将其绑定到变量。所以它是这样的:

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

因此,如果optional_expr的类型为T?,则non_optional_var的类型为T。 (当我写&#34; non_optional_var&#34;我并不意味着它不是可选的,而是表示它的可选性低于&#34; optional_expr& #34;。因此,如果non_optional_var的类型为Int?,则optional_expr的类型为Int??

顺便说一句,可选的绑定语法是用于启用Optional枚举的语法糖:

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