2016-01-20 90 views
1
// 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编译器总是很高兴,如果类型是'Void'以外的其他东西?T,可选<T>与太虚,可选<Void>

+0

'v2'只声明,试图给它分配一个值,看看如果编译器是确定与 – Cristik

+0

@Cristic V2的右边都有默认值为零 – user3441734

回答

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

这是合法的斯威夫特,但它生成一个“推断为有型‘()’”的警告。这是一个非常容易的错误。至少如果您尝试分配不返回结果的结果,则至少需要警告。

那么我们如何分配不返回结果的结果呢?这是因为每个函数都返回一个结果。如果退货是Void,我们只允许忽略这些信息。请务必记住Void并不意味着“无类型”或“无”。它只是()的一个typealias,它是零元素的元组。它与Int一样有效。

上述代码的完整形式是:

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

这将返回相同的警告,因为它是一回事。我们可以放弃->()return(),但它们存在,因此如果我们愿意,我们可以将()指定为x。但是我们想要的是不可能的。我们几乎肯定犯了一个错误,编译器就此警告我们。如果由于某种原因我们想要这种行为,那很好。这是合法的Swift。我们只需要更明确一些类型,而不是依赖于类型推断,并警告将消失:

let x: Void = foo() 

斯威夫特是在您的实例的产生警告非常一致,而且你真的希望这些警告。它根本不是任意的。


编辑:您加入不同示例:

var v = Optional<Void>() 

这将生成错误:ambiguous use of 'init()'。这是因为编译器不确定您的意思是Optional.init()将是.None还是Optional.init(_ some:()),这将是.Some(())。不明确的类型被禁止,所以你会得到一个硬性错误。

在Swift中,任何值都会隐式转换为等价的1元组。例如,1(1)是不同的类型。第一个是Int,第二个是包含Int的元组。但Swift会默默地为你转换这些(这就是为什么你有时会在错误信息中令人惊讶的地方看到括号出现)。所以foo()foo(())是一样的东西。在几乎所有可能的情况下,这并不重要。但在这种情况下,类型真的是(),这很重要,并使事情变得模糊。

var i = Optional<Int>() 

这明确地指Optional.init()并返回nil

+0

我明白你的意思,但空()或可选<()> .None方面与Any有点不同。任何()都没有意义,只是因为Any是协议并且不能被实例化。所以在你的例子x中。dynamicType可能会不同(将在运行时知道) – user3441734

+0

Rob,请你能比较这两句话吗? var v =可选(); var i =可选() – user3441734

+0

我删除了对“Any”的讨论。我记得有'Any'和'AnyObject'的特殊待遇,但是我最近的测试表明他们得到的待遇与其他协议相同。我不确定这是否是Swift后期版本的变化(我记得在Swift的早期版本中有一个关于'AnyObject'的具体警告),但现在看来它确实不是真的。 –

0

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

如果您没有明确指定您希望变量的类型为Void,并让编译器推断该类型,它会警告您这一点,因为它可能不是您想要的第一个地方。

例如:

func doSomething() -> Void { 
} 

let a = doSomething() 

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

在另一方面,

let a: Void = doSomething() 

不会产生警告,你明确地告诉你想要一个Void变量的编译器。

+0

这是发现,我发现相同。为什么与其他类型我们没有警告?顺便说一句,看看func f()抛出 - > Void和一些有用的用法,例如guard let v = try? f(){print(“func引发的错误f()”; return}或者如果让v = try?f(){} else {print(“f()引发的错误”}等等... – user3441734

相关问题