在Swift 3、为什么函数的void return值,->(),只是有时推断,而其他时候必须声明?
In Swift 3, why is the void return value of functions, -> (), only sometimes inferred, but other times, must be declared?
考虑以下代码:
typealias bar = ()->()
let foo:bar = { baz -> () in
print("foobar")
return ()
}
let baz = foo()
print("\(baz)")
输出:
foobar
()
现在,如果我们改变这一行:
let foo:bar = { baz -> () in
对此:
let foo:bar = { baz in
...然后什么也没有发生。即,-> ()
由编译器推断。 (注意:如果我们省略 return ()
,编译器也会很高兴。)
但是,与此同时,如果我们更改此行:
typealias bar = ()->()
对此:
typealias bar = ()
...然后编译器吓坏了,自杀了。为什么?
这似乎与 Swift 的文档冲突,该文档将 ()
(又名 Void
)定义为:
The return type of functions that don’t explicitly specify a return type
根据该声明,由于 typealias bar = ()->()
明确指定了 return 类型,因此根据定义,它不能 return ()
— 但它确实如此!这完全不合逻辑,自相矛盾。
有人可以向我解释一下他们对此的想法吗?这里的基本原理是什么?
如果 ->()
应该总是被推断出来那么为什么还要有它呢?为什么不直接说 ()
总是一个函数,这样 ()()
总是 returns ()
?
如果我没记错的话,你说的就是这种情况:
typealias bar = ()
let foo: bar = { baz -> () in
print("foobar")
return ()
}
赋值的右边是 () -> ()
类型的闭包,它是未计算的。 foo
应具有类型 bar (()
),但只有在评估闭包时才会出现这种情况,例如:
let foo: bar = { baz -> () in
print("foobar")
return ()
}()
我喜欢 ,但这是另一个可能有用的角度。
使用 typealias bar = ...
声明,您正在定义一个类型。该定义必须完整且明确。否则,类型系统将无法检查您稍后声明的内容是否是该类型的成员。这个声明提供了类型的完整签名——对于 function/closure 类型,这意味着它的参数类型集和它的 return 类型,即使两者都是 Void
(又名 ()
).
使用 let foo: bar = ...
您声明一个值并声明它必须是该类型的成员。因为类型的部分定义是已知的(多亏了类型别名),所以在声明类型的成员时不需要重复它们:
- 你的闭包不需要声明它的 return 类型,因为那是你的闭包符合的函数类型的一部分。您可以
return
闭包中预期类型的任何值。 (或者,由于您的函数类型定义了 Void
的 return 类型,因此您什么也不能 return
。)
- 您的闭包不需要声明其参数的类型,因为类型别名也已经这样做了。 (或者,因为你唯一的参数类型是
Void
,你根本不需要声明参数。)
在定义一个已知类型的闭包时省略一些闭包语法是Swift的类型推断特性之一。您可以将其视为等同于,如果您有一个 enum Foo { case one, two, three }
和一个 func bar(_ foo: Foo)
,您可以在调用 (bar(.one)
) 中仅传递 .one
。 Foo.one
是该常量的完全限定名称,但仅 .one
就足够了,因为 Swift 可以推断出 Foo
类型。
考虑以下代码:
typealias bar = ()->()
let foo:bar = { baz -> () in
print("foobar")
return ()
}
let baz = foo()
print("\(baz)")
输出:
foobar
()
现在,如果我们改变这一行:
let foo:bar = { baz -> () in
对此:
let foo:bar = { baz in
...然后什么也没有发生。即,-> ()
由编译器推断。 (注意:如果我们省略 return ()
,编译器也会很高兴。)
但是,与此同时,如果我们更改此行:
typealias bar = ()->()
对此:
typealias bar = ()
...然后编译器吓坏了,自杀了。为什么?
这似乎与 Swift 的文档冲突,该文档将 ()
(又名 Void
)定义为:
The return type of functions that don’t explicitly specify a return type
根据该声明,由于 typealias bar = ()->()
明确指定了 return 类型,因此根据定义,它不能 return ()
— 但它确实如此!这完全不合逻辑,自相矛盾。
有人可以向我解释一下他们对此的想法吗?这里的基本原理是什么?
如果 ->()
应该总是被推断出来那么为什么还要有它呢?为什么不直接说 ()
总是一个函数,这样 ()()
总是 returns ()
?
如果我没记错的话,你说的就是这种情况:
typealias bar = ()
let foo: bar = { baz -> () in
print("foobar")
return ()
}
赋值的右边是 () -> ()
类型的闭包,它是未计算的。 foo
应具有类型 bar (()
),但只有在评估闭包时才会出现这种情况,例如:
let foo: bar = { baz -> () in
print("foobar")
return ()
}()
我喜欢
使用 typealias bar = ...
声明,您正在定义一个类型。该定义必须完整且明确。否则,类型系统将无法检查您稍后声明的内容是否是该类型的成员。这个声明提供了类型的完整签名——对于 function/closure 类型,这意味着它的参数类型集和它的 return 类型,即使两者都是 Void
(又名 ()
).
使用 let foo: bar = ...
您声明一个值并声明它必须是该类型的成员。因为类型的部分定义是已知的(多亏了类型别名),所以在声明类型的成员时不需要重复它们:
- 你的闭包不需要声明它的 return 类型,因为那是你的闭包符合的函数类型的一部分。您可以
return
闭包中预期类型的任何值。 (或者,由于您的函数类型定义了Void
的 return 类型,因此您什么也不能return
。) - 您的闭包不需要声明其参数的类型,因为类型别名也已经这样做了。 (或者,因为你唯一的参数类型是
Void
,你根本不需要声明参数。)
在定义一个已知类型的闭包时省略一些闭包语法是Swift的类型推断特性之一。您可以将其视为等同于,如果您有一个 enum Foo { case one, two, three }
和一个 func bar(_ foo: Foo)
,您可以在调用 (bar(.one)
) 中仅传递 .one
。 Foo.one
是该常量的完全限定名称,但仅 .one
就足够了,因为 Swift 可以推断出 Foo
类型。