T,可选<t> vs. Void,可选<void>

时间:2016-01-20 15:28:34

标签: swift

// this declaration / definition of variable is OK, as expected
var i = Optional<Int>.None
var j:Int?

// for the next line of code compiler produce a nice warning
// Variable 'v1' inferred to have type 'Optional<Void>' (aka 'Optional<()>'), which may be unexpected
var v1 = Optional<Void>.None

// but the next sentence doesn't produce any warning
var v2:Void?

// nonoptional version produce the warning 'the same way'
// Variable 'v3' inferred to have type '()', which may be unexpected
var v3 = Void()

// but the compiler feels fine with the next
var v4: Void = Void()

有什么区别?为什么Swift编译器总是很开心,如果类型不是&#39; Void&#39; ?

2 个答案:

答案 0 :(得分:2)

警告中的关键词是“推断”。斯威夫特不喜欢推断Void,因为它通常不是你的意思。但是如果你明确地要求它(: Void)那么那很好,如果这就是你的意思,你将如何安静警告。

重要的是要识别哪些类型是推断的,哪些类型是明确的。推断是“从证据和推理中推断或得出(信息)而不是从明确的陈述中推断出来。”它不是“猜测”或“选择”的同义词。如果类型不明确,则编译器将生成错误。该类型必须始终定义良好。问题是它是显式定义,还是基于显式信息通过推理定义。

此声明有一个类型推断:

let x = Foo()

Foo()的类型是明确已知的,但x的类型是根据整个表达式的类型(Foo)推断出来的。它定义明确且完全明确,但它是推断出来的。

此声明没有类型推断:

let x: Foo = Foo()

但是,这里没有类型推论:

var x: Foo? = nil
x = Foo()

第二行中xFoo?)的类型是显式的,因为它是在上面的行中明确定义的。

这就是为什么你的一些例子会产生警告(当有Void推断时)而其他例子没有(当只有Void明确使用时)。为什么我们关心推断的Void?因为它很容易偶然发生,几乎从来没用过。例如:

func foo() {}
let x = foo()

这是合法的Swift,但它会生成“推断为具有类型'()'”警告。这是一个非常容易犯的错误。至少如果你试图分配一些不会返回结果的结果,你会想要一个警告。

那么我们如何分配不返回结果的结果呢?这是因为每个函数都会返回一个结果。如果返回Void,我们只允许省略该信息。重要的是要记住Void并不意味着“没有类型”或“没有”。它只是()的一个类型,它是零元素的元组。它与Int一样有效。

上述代码的完整形式为:

func foo() -> () { return () }
let x = foo()

这会返回相同的警告,因为它是一样的。我们被允许删除-> ()return (),但它们存在,因此如果我们愿意,我们可以将()分配给x。但我们不太可能想要这样做。我们几乎肯定犯了一个错误,编译器警告我们。如果由于某种原因我们想要这种行为,那很好。这是合法的斯威夫特。我们只需要明确类型而不是依赖类型推断,警告就会消失:

let x: Void = foo()

Swift在您的示例中生成警告非常一致,您确实需要这些警告。这根本不是任意的。

编辑:您添加了不同的示例:

var v = Optional<Void>()

这会生成错误:ambiguous use of 'init()'。这是因为编译器不确定您的Optional.init().None还是Optional.init(_ some: ()),而.Some(())1。禁止使用不明确的类型,因此会出现严重错误。

在Swift中,任何值都将隐含地转换为等效的1元组。例如,(1)Int是不同的类型。第一个是Int,第二个是包含foo()的元组。但是Swift会默默地为你转换这些(这就是为什么你有时看到括号在错误信息中出现在令人惊讶的地方)。所以foo(())()是一回事。在几乎所有可能的情况下,这都无关紧要。但是在这种情况下,类型确实是var i = Optional<Int>() ,这很重要并且使事情变得模棱两可。

Optional.init()

这明确指出nil并返回{{1}}。

答案 1 :(得分:0)

编译器警告您“虚拟”类型Void,它实际上是空元组()的别名,并且没有太多用法。

如果你没有明确指出你希望你的变量是Void类型,并让编译器推断出类型,它会警告你这个,因为它可能是你不想要的首先要这样做。

例如:

func doSomething() -> Void {
}

let a = doSomething()
无论如何,

会给你一个警告,只有一个可能的值doSomething()可以返回 - 一个空元组。

另一方面,

let a: Void = doSomething()
当您明确告诉编译器您需要Void变量时,

不会生成警告。