2016-01-20 55 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

相關問題