Let's say we have:
假设我们有:
let a:Int? = nil
// block not executed - unwapping done, type is inferred
if let unwrapped_a = a {
println(unwrapped_a)
}
// block not executed - unwrapping done, type is specified
if let unwrapped_a:Int = a {
println(unwrapped_a)
}
// block gets executed - unwrapping not done, new local constant + assignment is done instead?
if let not_unwrapped_a:Int? = a {
println(not_unwrapped_a)
}
So should I assume that Swift does an unwrapping in the first case, but an assignment in the second case?
那么我是否应该假设Swift在第一种情况下进行了解包,而在第二种情况下进行了分配?
Isn't this syntax a bit too close to create confusion? I mean, yes, the compiler warns you that you're using an optional type when working with not_unwrapped_a
, but still.
这种语法有点过于接近而不会产生混淆吗?我的意思是,是的,编译器警告您在使用not_unwrapped_a时使用的是可选类型,但仍然如此。
Update:
So after Airspeed Velocity's answer I found another (but actually the same) weird case:
所以在Airspeed Velocity的回答之后,我发现另一个(但实际上是相同的)奇怪的情况:
if let not_unwrapped_a:Int???? = a {
println(not_unwrapped_a)
}
a
will be silently wrapped in an Int????
. So it will be a type of Int?????
(five) - because a
was already an optional. And then it will get unwrapped once.
a将默默地包裹在Int ????中。所以这将是一种Int ????? (五) - 因为a已经是可选的。然后它将被解开一次。
2 个解决方案
#1
5
Case 1 and case 2 are identical – they are both assignments of the contents of a
to a new variable. The only difference is you are leaving Swift to infer the type of unwrapped_a
in option 1, whereas you’re manually giving the type in option 2. The main reason you’d ever need to do option 2 is if the source value were ambiguous – for example if it were an overloaded function that could return multiple types.
案例1和案例2是相同的 - 它们都是a对新变量的内容的赋值。唯一的区别是你要离开Swift来推断选项1中unwrapped_a的类型,而你手动给出选项2中的类型。你需要做选项2的主要原因是如果源值不明确 - 例如,如果它是一个可以返回多种类型的重载函数。
Case 3 is quite interesting.
案例3非常有趣。
Whenever you have a value, Swift will always be willing to silently upgrade it to an optional wrapping the value, if it helps make the types match and the code compile. Swift auto-upgrading of types is fairly rare (it won’t implicitly upgrade an Int16
to an Int32
for example) but values to optionals is an exception.
每当你有一个值时,Swift总是愿意默默地将它升级为可选的包装值,如果它有助于使类型匹配并且代码编译。类型的Swift自动升级是相当罕见的(例如,它不会隐式地将Int16升级为Int32),但是对optionals的值是一个例外。
This means you can pass values wherever an optional is needed without having to bother to wrap it:
这意味着您可以在需要可选项的地方传递值,而无需费心去包装它:
func f(maybe: Int?) { ... }
let i = 1
// you can just pass a straight value:
f(i)
// Swift will turn this into this:
f(Optional(i))
So in your final example, you’ve told Swift you want not_unwrapped_a
to be an Int?
. But it’s part of a let
that requires a
to be unwrapped before it’s assigned to it.
所以在你的最后一个例子中,你告诉Swift你想要not_unwrapped_a成为一个Int?。但它是let的一部分,需要在分配给它之前将其解包。
Presented with this, the only way Swift can make it work is to implicitly wrap a
in another optional, so that’s what it does. Now it is an optional containing an optional containing nil. That is not a nil-valued optional - that is an optional containing a value (of an optional containing nil). Unwrapping that gives you an optional containing nil. It seems like nothing happened. But it did - it got wrapped a second time, then unwrapped once.
提出这一点,Swift可以使其工作的唯一方法是隐式地包装另一个可选项,这就是它的作用。现在它是一个可选项,包含一个包含nil的可选项。这不是一个零值可选 - 这是一个包含值的可选项(包含nil的可选值)。展开会给你一个包含nil的可选项。好像什么都没发生。但它确实 - 它被包裹了第二次,然后解开了一次。
You can see this in action if you compile your sample code with swiftc -dump-ast source.swift
. You’ll see the phrase inject_into_optional implicit type='Int??’
. The Int??
is an optional containing an optional.
如果使用swiftc -dump-ast source.swift编译示例代码,则可以看到此操作。你会看到短语inject_into_optional implicit type ='Int ??'。 Int ?? ??是一个包含可选项的可选项。
Optionals containing optionals aren’t obscure edge cases - they can happen easily. For example if you ever for…in over an array containing optionals, or used a subscript to get a value out of a dictionary that contained optionals, then optionals of optionals have been involved in that process.
包含选项的选项不是模糊的边缘情况 - 它们很容易发生。例如,如果你曾经...在包含选项的数组中,或者使用下标从包含选项的字典中获取值,则选项的选项已经参与该过程。
Another way of thinking about this is if you think of if let x = y { }
as kind of* like a function, if_let
, defined as follows:
考虑这个问题的另一种方法是,如果你考虑让x = y {}作为*类似函数if_let,定义如下:
func if_let<T>(optVal: T?, block: T->()) {
if optVal != nil {
block(optVal!)
}
}
Now imagine if you supplied a block
that took an Int?
– that is, T
would be an Int?
. So T?
would be a Int??
. When you passed a regular Int?
into if_let
along with that block, Swift would then implicitly upgrade it to an Int??
to make it compile. That’s essentially what’s happening with the if let not_unwrapped_a:Int?
.
现在假设您提供了一个采用Int的块? - 也就是说,T将是一个Int?。那么T?将是一个Int ??。当你通过常规的Int?进入if_let和那个块,然后Swift会隐式地将它升级为Int ??使它编译。这基本上是if let not_unwrapped_a:Int?正在发生的事情。
I agree, the implicit optional upgrade can sometimes be surprising (even more surprising is that Swift will upgrade functions that return optionals, i.e. if a function takes an (Int)->Int?
, it will upgrade an (Int)->Int
to return an optional instead). But presumably the feeling is the potential confusion is worth it for the convenience in this case.
我同意,隐式可选升级有时会令人惊讶(更令人惊讶的是Swift将升级返回选项的函数,即如果函数采用(Int) - > Int ?,它会升级(Int) - > Int到返回一个可选的)。但据推测,感觉是潜在的混乱是值得的,因为在这种情况下方便。
* only kind of
*只有一种
#2
4
The purpose of the optional binding is to check an optional for not nil, unwrap and assign to a non optional of the type enclosed in the optional. So in my opinion the 1st case is the correct way of using it - the only variation I would use is adding an optional cast (useful for example when the optional contains AnyObject
).
可选绑定的目的是检查可选的for nil,unwrap并赋值给可选中包含的非可选类型。因此,在我看来,第一种情况是使用它的正确方法 - 我将使用的唯一变体是添加可选的强制转换(例如,当可选项包含AnyObject时很有用)。
I wouldn't use the 2nd case, preferring an optional cast:
我不会使用第二种情况,更喜欢可选的演员:
if let unwrapped_a = a as? Int { ... }
unless, as noted by @drewag in comments, the type is explicitly specified to avoid ambiguities when it's not clear.
除非@drewag在评论中指出,否则明确指定类型以避免在不清楚时出现歧义。
In my opinion the 3rd case should generate a compilation error. I don't see any use of optional binding to assign an optional to another optional. Optional binding is not a generic inline assignment (such as if let x = 5 {...}
), so conceptually it should not work if the left side is an optional - it should be handled the same way as if the right side is not an optional (for which compilation fails instead).
在我看来,第三种情况应该生成编译错误。我没有看到任何使用可选绑定将可选项分配给另一个可选项。可选绑定不是通用的内联赋值(例如,如果让x = 5 {...}),所以从概念上讲,如果左侧是可选的,它应该不起作用 - 它的处理方式应该与右侧是相同的不是可选的(相反,编译失败)。
#1
5
Case 1 and case 2 are identical – they are both assignments of the contents of a
to a new variable. The only difference is you are leaving Swift to infer the type of unwrapped_a
in option 1, whereas you’re manually giving the type in option 2. The main reason you’d ever need to do option 2 is if the source value were ambiguous – for example if it were an overloaded function that could return multiple types.
案例1和案例2是相同的 - 它们都是a对新变量的内容的赋值。唯一的区别是你要离开Swift来推断选项1中unwrapped_a的类型,而你手动给出选项2中的类型。你需要做选项2的主要原因是如果源值不明确 - 例如,如果它是一个可以返回多种类型的重载函数。
Case 3 is quite interesting.
案例3非常有趣。
Whenever you have a value, Swift will always be willing to silently upgrade it to an optional wrapping the value, if it helps make the types match and the code compile. Swift auto-upgrading of types is fairly rare (it won’t implicitly upgrade an Int16
to an Int32
for example) but values to optionals is an exception.
每当你有一个值时,Swift总是愿意默默地将它升级为可选的包装值,如果它有助于使类型匹配并且代码编译。类型的Swift自动升级是相当罕见的(例如,它不会隐式地将Int16升级为Int32),但是对optionals的值是一个例外。
This means you can pass values wherever an optional is needed without having to bother to wrap it:
这意味着您可以在需要可选项的地方传递值,而无需费心去包装它:
func f(maybe: Int?) { ... }
let i = 1
// you can just pass a straight value:
f(i)
// Swift will turn this into this:
f(Optional(i))
So in your final example, you’ve told Swift you want not_unwrapped_a
to be an Int?
. But it’s part of a let
that requires a
to be unwrapped before it’s assigned to it.
所以在你的最后一个例子中,你告诉Swift你想要not_unwrapped_a成为一个Int?。但它是let的一部分,需要在分配给它之前将其解包。
Presented with this, the only way Swift can make it work is to implicitly wrap a
in another optional, so that’s what it does. Now it is an optional containing an optional containing nil. That is not a nil-valued optional - that is an optional containing a value (of an optional containing nil). Unwrapping that gives you an optional containing nil. It seems like nothing happened. But it did - it got wrapped a second time, then unwrapped once.
提出这一点,Swift可以使其工作的唯一方法是隐式地包装另一个可选项,这就是它的作用。现在它是一个可选项,包含一个包含nil的可选项。这不是一个零值可选 - 这是一个包含值的可选项(包含nil的可选值)。展开会给你一个包含nil的可选项。好像什么都没发生。但它确实 - 它被包裹了第二次,然后解开了一次。
You can see this in action if you compile your sample code with swiftc -dump-ast source.swift
. You’ll see the phrase inject_into_optional implicit type='Int??’
. The Int??
is an optional containing an optional.
如果使用swiftc -dump-ast source.swift编译示例代码,则可以看到此操作。你会看到短语inject_into_optional implicit type ='Int ??'。 Int ?? ??是一个包含可选项的可选项。
Optionals containing optionals aren’t obscure edge cases - they can happen easily. For example if you ever for…in over an array containing optionals, or used a subscript to get a value out of a dictionary that contained optionals, then optionals of optionals have been involved in that process.
包含选项的选项不是模糊的边缘情况 - 它们很容易发生。例如,如果你曾经...在包含选项的数组中,或者使用下标从包含选项的字典中获取值,则选项的选项已经参与该过程。
Another way of thinking about this is if you think of if let x = y { }
as kind of* like a function, if_let
, defined as follows:
考虑这个问题的另一种方法是,如果你考虑让x = y {}作为*类似函数if_let,定义如下:
func if_let<T>(optVal: T?, block: T->()) {
if optVal != nil {
block(optVal!)
}
}
Now imagine if you supplied a block
that took an Int?
– that is, T
would be an Int?
. So T?
would be a Int??
. When you passed a regular Int?
into if_let
along with that block, Swift would then implicitly upgrade it to an Int??
to make it compile. That’s essentially what’s happening with the if let not_unwrapped_a:Int?
.
现在假设您提供了一个采用Int的块? - 也就是说,T将是一个Int?。那么T?将是一个Int ??。当你通过常规的Int?进入if_let和那个块,然后Swift会隐式地将它升级为Int ??使它编译。这基本上是if let not_unwrapped_a:Int?正在发生的事情。
I agree, the implicit optional upgrade can sometimes be surprising (even more surprising is that Swift will upgrade functions that return optionals, i.e. if a function takes an (Int)->Int?
, it will upgrade an (Int)->Int
to return an optional instead). But presumably the feeling is the potential confusion is worth it for the convenience in this case.
我同意,隐式可选升级有时会令人惊讶(更令人惊讶的是Swift将升级返回选项的函数,即如果函数采用(Int) - > Int ?,它会升级(Int) - > Int到返回一个可选的)。但据推测,感觉是潜在的混乱是值得的,因为在这种情况下方便。
* only kind of
*只有一种
#2
4
The purpose of the optional binding is to check an optional for not nil, unwrap and assign to a non optional of the type enclosed in the optional. So in my opinion the 1st case is the correct way of using it - the only variation I would use is adding an optional cast (useful for example when the optional contains AnyObject
).
可选绑定的目的是检查可选的for nil,unwrap并赋值给可选中包含的非可选类型。因此,在我看来,第一种情况是使用它的正确方法 - 我将使用的唯一变体是添加可选的强制转换(例如,当可选项包含AnyObject时很有用)。
I wouldn't use the 2nd case, preferring an optional cast:
我不会使用第二种情况,更喜欢可选的演员:
if let unwrapped_a = a as? Int { ... }
unless, as noted by @drewag in comments, the type is explicitly specified to avoid ambiguities when it's not clear.
除非@drewag在评论中指出,否则明确指定类型以避免在不清楚时出现歧义。
In my opinion the 3rd case should generate a compilation error. I don't see any use of optional binding to assign an optional to another optional. Optional binding is not a generic inline assignment (such as if let x = 5 {...}
), so conceptually it should not work if the left side is an optional - it should be handled the same way as if the right side is not an optional (for which compilation fails instead).
在我看来,第三种情况应该生成编译错误。我没有看到任何使用可选绑定将可选项分配给另一个可选项。可选绑定不是通用的内联赋值(例如,如果让x = 5 {...}),所以从概念上讲,如果左侧是可选的,它应该不起作用 - 它的处理方式应该与右侧是相同的不是可选的(相反,编译失败)。