make loop

This commit is contained in:
chai2010
2015-12-26 20:05:30 +08:00
parent 82ec0c025d
commit e15e88dad7
74 changed files with 207 additions and 207 deletions

View File

@@ -29,7 +29,7 @@ continue for import return var
這些內部預先定義的名字併不是關鍵字,你可以再定義中重新使用它們。在一些特殊的場景中重新定義它們也是有意義的,但是也要註意避免過度而引起語義混亂。
如果一個名字是在函數內部定義,那麽它的就隻在函數內部有效。如果是在函數外部定義,那麽將在當前包的所有文件中都可以訪問。名字的開頭字母的大小寫決定了名字在包外的可見性。如果一個名字是大寫字母開頭的(譯註:必鬚是在函數外部定義的包級名字;包級函數名本身也是包級名字),那麽它將是導也就是説可以被外部的包訪問例如fmt包的Printf函數就是導可以在fmt包外部訪問。包本身的名字一般總是用小寫字母。
如果一個名字是在函數內部定義,那麽它的就隻在函數內部有效。如果是在函數外部定義,那麽將在當前包的所有文件中都可以訪問。名字的開頭字母的大小寫決定了名字在包外的可見性。如果一個名字是大寫字母開頭的(譯註:必鬚是在函數外部定義的包級名字;包級函數名本身也是包級名字),那麽它將是導也就是説可以被外部的包訪問例如fmt包的Printf函數就是導可以在fmt包外部訪問。包本身的名字一般總是用小寫字母。
名字的長度沒有邏輯限製但是Go語言的風格是盡量使用短小的名字對於局部變量尤其是這樣你會經常看到i之類的短名字而不是冗長的theLoopIndex命名。通常來説如果一個名字的作用域比較大生命週期也比較長那麽用長的名字將會更有意義。

View File

@@ -2,7 +2,7 @@
聲明語句定義了程序的各種實體對象以及部分或全部的屬性。Go語言主要有四種類型的聲明語句var、const、type和func分别對應變量、常量、類型和函數實體對象的聲明。這一章我們重點討論變量和類型的聲明第三章將討論常量的聲明第五章將討論函數的聲明。
一個Go語言編寫的程序對應一個或多個以.go爲文件後綴名的源文件中。每個源文件以包的聲明語句開始説明該源文件是屬於哪個包。包聲明語句之後是import語句導入依賴的其它包然後是包一級的類型、變量、常量、函數的聲明語句包一級的各種類型的聲明語句的順序無關緊要譯註函數內部的名字則必鬚先聲明之後能使用)。例如,下面的例子中聲明了一個常量、一個函數和兩個變量:
一個Go語言編寫的程序對應一個或多個以.go爲文件後綴名的源文件中。每個源文件以包的聲明語句開始説明該源文件是屬於哪個包。包聲明語句之後是import語句導入依賴的其它包然後是包一級的類型、變量、常量、函數的聲明語句包一級的各種類型的聲明語句的順序無關緊要譯註函數內部的名字則必鬚先聲明之後能使用)。例如,下面的例子中聲明了一個常量、一個函數和兩個變量:
```Go
gopl.io/ch2/boiling

View File

@@ -63,7 +63,7 @@ f, err := os.Create(outfile) // compile error: no new variables
解決的方法是第二個簡短變量聲明語句改用普通的多重賦值語言。
簡短變量聲明語句隻有對已經在同級詞法域聲明過的變量和賦值操作語句等價,如果變量是在外部詞法域聲明的,那麽簡短變量聲明語句將會在當前詞法域重新聲明一個新的變量。我們在本章後面將會看到類似的例子。
簡短變量聲明語句隻有對已經在同級詞法域聲明過的變量和賦值操作語句等價,如果變量是在外部詞法域聲明的,那麽簡短變量聲明語句將會在當前詞法域重新聲明一個新的變量。我們在本章後面將會看到類似的例子。

View File

@@ -1,10 +1,10 @@
### 2.3.2. 指針
一個變量對應一個保存了變量對應類型值的內存空間。普通變量在聲明語句創建時被綁定到一個變量名比如叫x的變量但是還有很多變量始終以表達式方式引入例如x[i]或x.f變量。所有這些表達式一般都是讀取一個變量的值除非它們是現在賦值語句的左邊,這種時候是給對應變量賦予一個新的值。
一個變量對應一個保存了變量對應類型值的內存空間。普通變量在聲明語句創建時被綁定到一個變量名比如叫x的變量但是還有很多變量始終以表達式方式引入例如x[i]或x.f變量。所有這些表達式一般都是讀取一個變量的值除非它們是現在賦值語句的左邊,這種時候是給對應變量賦予一個新的值。
一個指針的值是另一個變量的地址。一個指針對應變量在內存中的存儲位置。併不是每一個值都會有一個內存地址,但是對於每一個變量必然有對應的內存地址。通過指針,我們可以直接讀或更新對應變量的值,而不需要知道該變量的名字(如果變量有名字的話)。
如果用“var x int”聲明語句聲明一個x變量那麽&x表達式取x變量的內存地址將産生一個指向該整數變量的指針指針對應的數據類型是`*int`指針被稱之爲“指向int類型的指針”。如果指針名字爲p那麽可以説“p指針指向變量x”或者説“p指針保存了x變量的內存地址”。同時`*p`表達式對應p指針指向的變量的值。一般`*p`表達式讀取指針指向的變量的值這里爲int類型的值同時因爲`*p`對應一個變量,所以該表達式也可以現在賦值語句的左邊,表示更新指針所指向的變量的值。
如果用“var x int”聲明語句聲明一個x變量那麽&x表達式取x變量的內存地址將産生一個指向該整數變量的指針指針對應的數據類型是`*int`指針被稱之爲“指向int類型的指針”。如果指針名字爲p那麽可以説“p指針指向變量x”或者説“p指針保存了x變量的內存地址”。同時`*p`表達式對應p指針指向的變量的值。一般`*p`表達式讀取指針指向的變量的值這里爲int類型的值同時因爲`*p`對應一個變量,所以該表達式也可以現在賦值語句的左邊,表示更新指針所指向的變量的值。
```Go
x := 1
@@ -18,7 +18,7 @@ fmt.Println(x) // "2"
變量有時候被稱爲可尋址的值。卽使變量由表達式臨時生成,那麽表達式也必鬚能接受`&`取地址操作。
任何類型的指針的零值都是nil。如果`p != nil`測試爲眞那麽p是指向某個有效變量。指針之間也是可以進行相等測試的隻有當它們指向同一個變量或全部是nil時相等。
任何類型的指針的零值都是nil。如果`p != nil`測試爲眞那麽p是指向某個有效變量。指針之間也是可以進行相等測試的隻有當它們指向同一個變量或全部是nil時相等。
```Go
var x, y int

View File

@@ -30,7 +30,7 @@ for t := 0.0; t < cycles*2*math.Pi; t += res {
那麽垃Go語言的自動圾收集器是如何知道一個變量是何時可以被迴收的呢這里我們可以避開完整的技術細節基本的實現思路是從每個包級的變量和每個當前運行函數的每一個局部變量開始通過指針或引用的訪問路徑遍歷是否可以找到該變量。如果不存在這樣的訪問路徑那麽説明該變量是不可達的也就是説它是否存在併不會影響程序後續的計算結果。
因爲一個變量的有效週期隻取決於是否可達,因此一個循環迭代內部的局部變量的生命週期可能超其局部作用域。同時,局部變量可能在函數返迴之後依然存在。
因爲一個變量的有效週期隻取決於是否可達,因此一個循環迭代內部的局部變量的生命週期可能超其局部作用域。同時,局部變量可能在函數返迴之後依然存在。
編譯器會自動選擇在棧上還是在堆上分配局部變量的存儲空間但可能令人驚訝的是這個選擇併不是由用var還是new聲明變量的方式決定的。
@@ -44,7 +44,7 @@ func f() { func g() {
}
```
這里的x變量必鬚在堆上分配因爲它在函數退後依然可以通過包一級的global變量找到雖然它是在函數內部定義的用Go語言的術語説這個x局部變量從函數f中逃逸了。相反當g函數返迴時變量`*y`將是不可達的,也就是説可以馬上被迴收的。因此,`*y`併沒有從函數g中逃逸編譯器可以選擇在棧上分配`*y`的存儲空間譯註也可以選擇在堆上分配然後由Go語言的GC迴收這個變量的內存空間雖然這里用的是new方式。其實在任何時候你併不需爲了編寫正確的代碼而要考慮變量的逃逸行爲要記住的是逃逸的變量需要額外分配內存同時對性能的優化可能會産生細微的影響。
這里的x變量必鬚在堆上分配因爲它在函數退後依然可以通過包一級的global變量找到雖然它是在函數內部定義的用Go語言的術語説這個x局部變量從函數f中逃逸了。相反當g函數返迴時變量`*y`將是不可達的,也就是説可以馬上被迴收的。因此,`*y`併沒有從函數g中逃逸編譯器可以選擇在棧上分配`*y`的存儲空間譯註也可以選擇在堆上分配然後由Go語言的GC迴收這個變量的內存空間雖然這里用的是new方式。其實在任何時候你併不需爲了編寫正確的代碼而要考慮變量的逃逸行爲要記住的是逃逸的變量需要額外分配內存同時對性能的優化可能會産生細微的影響。
Go語言的自動垃圾收集器對編寫正確的代碼是一個鉅大的幫助但也併不是説你完全不用考慮內存了。你雖然不需要顯式地分配和釋放內存但是要編寫高效的程序你依然需要了解變量的生命週期。例如如果將指向短生命週期對象的指針保存到具有長生命週期的對象中特别是保存到全局變量時會阻止對短生命週期對象的垃圾迴收從而可能影響程序的性能

View File

@@ -1,6 +1,6 @@
### 2.4.1. 元組賦值
元組賦值是另一種形式的賦值語句,它允許同時更新多個變量的值。在賦值之前,賦值語句右邊的所有表達式將會先進行求值,然後再統一更新左邊對應變量的值。這對於處理有些同時現在元組賦值語句左右兩邊的變量很有幫助,例如我們可以這樣交換兩個變量的值:
元組賦值是另一種形式的賦值語句,它允許同時更新多個變量的值。在賦值之前,賦值語句右邊的所有表達式將會先進行求值,然後再統一更新左邊對應變量的值。這對於處理有些同時現在元組賦值語句左右兩邊的變量很有幫助,例如我們可以這樣交換兩個變量的值:
```Go
x, y = y, x
@@ -39,13 +39,13 @@ i, j, k = 2, 3, 5
但如果表達式太複雜的話,應該盡量避免過度使用元組賦值;因爲每個變量單獨賦值語句的寫法可讀性會更好。
有些表達式會産生多個值,比如調用一個有多個返迴值的函數。當這樣一個函數調用現在元組賦值右邊的表達式中時(譯註:右邊不能再有其它表達式),左邊變量的數目必鬚和右邊一致。
有些表達式會産生多個值,比如調用一個有多個返迴值的函數。當這樣一個函數調用現在元組賦值右邊的表達式中時(譯註:右邊不能再有其它表達式),左邊變量的數目必鬚和右邊一致。
```Go
f, err = os.Open("foo.txt") // function call returns two values
```
通常這類函數會用額外的返迴值來表達某種錯誤類型例如os.Open是用額外的返迴值返迴一個error類型的錯誤還有一些是用來返迴布爾值通常被稱爲ok。在稍後我們將看到的三個操作都是類似的用法。如果map査找§4.3、類型斷言§7.10或通道接收§8.4.2現在賦值語句的右邊,它們都可能會産生兩個結果,有一個額外的布爾結果表示操作是否成功:
通常這類函數會用額外的返迴值來表達某種錯誤類型例如os.Open是用額外的返迴值返迴一個error類型的錯誤還有一些是用來返迴布爾值通常被稱爲ok。在稍後我們將看到的三個操作都是類似的用法。如果map査找§4.3、類型斷言§7.10或通道接收§8.4.2現在賦值語句的右邊,它們都可能會産生兩個結果,有一個額外的布爾結果表示操作是否成功:
```Go
v, ok = m[key] // map lookup
@@ -53,7 +53,7 @@ v, ok = x.(T) // type assertion
v, ok = <-ch // channel receive
```
譯註map査找§4.3、類型斷言§7.10或通道接收§8.4.2現在賦值語句的右邊時併不一定是産生兩個結果也可能隻産生一個結果。對於值産生一個結果的情形map査找失敗時會返迴零值類型斷言失敗時會發送運行時panic異常通道接收失敗時會返迴零值阻塞不算是失敗。例如下面的例子
譯註map査找§4.3、類型斷言§7.10或通道接收§8.4.2現在賦值語句的右邊時併不一定是産生兩個結果也可能隻産生一個結果。對於值産生一個結果的情形map査找失敗時會返迴零值類型斷言失敗時會發送運行時panic異常通道接收失敗時會返迴零值阻塞不算是失敗。例如下面的例子
```Go
v = m[key] // map査找失敗時返迴零值

View File

@@ -16,7 +16,7 @@ medals[2] = "bronze"
map和chan的元素雖然不是普通的變量但是也有類似的隱式賦值行爲。
不管是隱式還是顯式地賦值,在賦值語句左邊的變量和右邊最終的求到的值必鬚有相同的數據類型。更直白地説,隻有右邊的值對於左邊的變量是可賦值的,賦值語句是允許的。
不管是隱式還是顯式地賦值,在賦值語句左邊的變量和右邊最終的求到的值必鬚有相同的數據類型。更直白地説,隻有右邊的值對於左邊的變量是可賦值的,賦值語句是允許的。
可賦值性的規則對於不同類型有着不同要求對每個新類型特殊的地方我們會專門解釋。對於目前我們已經討論過的類型它的規則是簡單的類型必鬚完全匹配nil可以賦值給任何指針或引用類型的變量。常量§3.6)則有更靈活的賦值規則,因爲這樣可以避免不必要的顯式的類型轉換。

View File

@@ -10,9 +10,9 @@
type 類型名字 底層類型
```
類型聲明語句一般現在包一級,因此如果新創建的類型名字的首字符大寫,則在外部包也可以使用。
類型聲明語句一般現在包一級,因此如果新創建的類型名字的首字符大寫,則在外部包也可以使用。
譯註對於中文漢字Unicode標誌都作爲小寫字母處理因此中文的命名默認不能導;不過國內的用戶針對該問題提了我們自己的間接根據RobPike的迴複在Go2中有可能會將中日韓等字符當作大寫字母處理。下面是RobPik在 [Issue763](https://github.com/golang/go/issues/5763) 的迴複:
譯註對於中文漢字Unicode標誌都作爲小寫字母處理因此中文的命名默認不能導;不過國內的用戶針對該問題提了我們自己的間接根據RobPike的迴複在Go2中有可能會將中日韓等字符當作大寫字母處理。下面是RobPik在 [Issue763](https://github.com/golang/go/issues/5763) 的迴複:
> A solution that's been kicking around for a while:
>
@@ -41,13 +41,13 @@ func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
```
在這個包明了兩種類型Celsius和Fahrenheit分别對應不同的溫度單位。它們然有着相同的底層類型float64但是它們是不同的數據類型因此它們不可以被相互比較或混在一個表達式算。刻意區分類型,可以避免一些像無意中使用不同單位的溫度混合計算致的錯誤因此需要一個類似Celsius(t)或Fahrenheit(t)形式的顯式轉型操作能將float64轉爲對應的類型。Celsius(t)和Fahrenheit(t)是類型轉換操作,它併不是函數調用。類型轉換不會改變值本身但是會使它們的語義發生變化。另一方面CToF和FToC两个函数則是對不同溫度單位下的温度进行换算,它們會返迴不同的值。
在這個包明了兩種類型Celsius和Fahrenheit分别對應不同的溫度單位。它們然有着相同的底層類型float64但是它們是不同的數據類型因此它們不可以被相互比較或混在一個表達式算。刻意區分類型,可以避免一些像無意中使用不同單位的溫度混合計算致的錯誤因此需要一個類似Celsius(t)或Fahrenheit(t)形式的顯式轉型操作能將float64轉爲對應的類型。Celsius(t)和Fahrenheit(t)是類型轉換操作,它併不是函數調用。類型轉換不會改變值本身但是會使它們的語義發生變化。另一方面CToF和FToC兩個函數則是對不同溫度單位下的溫度進行換算,它們會返迴不同的值。
對於每一個類型T都有一個對應的類型轉換操作T(x)用於將x轉爲T類型译注如果T是指针类型,可能需要用小括弧包T比如`(*int)(0)`。隻有當兩個類型的底層基礎類型相同時才允許這種轉型操作或者是兩者都是指向相同底層結構的指針類型這些轉換隻改變類型而不會影響值本身。如果x是可以賦值給T類型的值那麽x必然也可以被轉爲T類型但是一般沒有这个必要。
對於每一個類型T都有一個對應的類型轉換操作T(x)用於將x轉爲T類型譯註如果T是指針類型,可能需要用小括弧包T比如`(*int)(0)`。隻有當兩個類型的底層基礎類型相同時才允許這種轉型操作或者是兩者都是指向相同底層結構的指針類型這些轉換隻改變類型而不會影響值本身。如果x是可以賦值給T類型的值那麽x必然也可以被轉爲T類型但是一般沒有這個必要。
數值類型之間的轉型也是允許的,併且在字符串和一些特定型的slice之間也是可以轉換的在下一章我們會看到這樣的例子。這類轉換可能改變值的表現。例如將一個浮點數轉爲整數將丟棄小數部分將一個字符串轉爲`[]byte`型的slice將拷貝一個字符串數據的副本。在任何情況下運行時不會發生轉換失敗的錯誤譯註: 錯誤隻會發生在編譯階段)。
數值類型之間的轉型也是允許的,併且在字符串和一些特定型的slice之間也是可以轉換的在下一章我們會看到這樣的例子。這類轉換可能改變值的表現。例如將一個浮點數轉爲整數將丟棄小數部分將一個字符串轉爲`[]byte`型的slice將拷貝一個字符串數據的副本。在任何情況下運行時不會發生轉換失敗的錯誤譯註: 錯誤隻會發生在編譯階段)。
底層數據類型決定了內部結構和表達方式也決定是否可以像底層類型一樣對內置運算符的支持。這意味着Celsius和Fahrenheit類型的算術算行爲和底層的float64類型是一樣的正如我所期望的那
底層數據類型決定了內部結構和表達方式也決定是否可以像底層類型一樣對內置運算符的支持。這意味着Celsius和Fahrenheit類型的算術算行爲和底層的float64類型是一樣的正如我所期望的那
```Go
fmt.Printf("%g\n", BoilingC-FreezingC) // "100" °C
@@ -69,17 +69,17 @@ fmt.Println(c == Celsius(f)) // "true"!
註意最後那個語句。盡管看起來想函數調用但是Celsius(f)是類型轉換操作它併不會改變值僅僅是改變值的類型而已。測試爲眞的原因是因爲c和g都是零值。
一個命名的類型可以提供书写方便特别是可以避免一遍又一遍地書寫複雜類型譯註例如用匿名的結構體定義變量。雖然對於像float64這種簡單的底層類型沒有簡潔很多但是如果是複雜的類型將會簡潔很多特别是我們卽將討論的結構體類型。
一個命名的類型可以提供書寫方便特别是可以避免一遍又一遍地書寫複雜類型譯註例如用匿名的結構體定義變量。雖然對於像float64這種簡單的底層類型沒有簡潔很多但是如果是複雜的類型將會簡潔很多特别是我們卽將討論的結構體類型。
命名類型還可以爲該類型的值定義新的行。這些行爲表示爲一組關聯到類型的函數集合,我們爲類型的方法集。我們將在第六章中討論方法的細節,這里值説寫簡單用法。
命名類型還可以爲該類型的值定義新的行。這些行爲表示爲一組關聯到類型的函數集合,我們爲類型的方法集。我們將在第六章中討論方法的細節,這里值説寫簡單用法。
下面的聲明Celsius類型的參數c現在了函數名的前面表示聲明的是Celsius類型的一叫名叫String的方法方法返迴该类型对象c帶着°C溫度單位的字符串
下面的聲明Celsius類型的參數c現在了函數名的前面表示聲明的是Celsius類型的一叫名叫String的方法方法返迴該類型對象c帶着°C溫度單位的字符串
```Go
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
```
許多類型都會定義一個String方法因爲當使用fmt包的打印方法時將會優先使用该类型对应的String方法返迴的結果打印將在7.1節講述。
許多類型都會定義一個String方法因爲當使用fmt包的打印方法時將會優先使用該類型對應的String方法返迴的結果打印將在7.1節講述。
```Go
c := FToC(212.0)

View File

@@ -34,7 +34,7 @@ func main() {
}
```
導入聲明將導入的包綁定到一個短小的名字, 然後通過該名字就可以引用包中導的全部內容. 上面的導入聲明將允許我們以 tempconv.CToF 的方式來訪問 gopl.io/ch2/tempconv 包中的內容. 默認情況下, 導入的包綁定到 tempconv 名字, 但是我們也可以綁定到另一個名稱, 以避免名字衝突(§10.3).
導入聲明將導入的包綁定到一個短小的名字, 然後通過該名字就可以引用包中導的全部內容. 上面的導入聲明將允許我們以 tempconv.CToF 的方式來訪問 gopl.io/ch2/tempconv 包中的內容. 默認情況下, 導入的包綁定到 tempconv 名字, 但是我們也可以綁定到另一個名稱, 以避免名字衝突(§10.3).
cf 程序將命令行輸入的一個溫度在 Celsius 和 Fahrenheit 之間轉換:
@@ -48,7 +48,7 @@ $ ./cf -40
-40°F = -40°C, -40°C = -40°F
```
如果導入一個包, 但是沒有使用該包將被當作一個錯誤. 這種強製檢測可以有效減少不必要的依賴, 雖然在調試期間會讓人討厭, 因爲刪除一個類似 log.Print("got here!") 的打印可能導致需要同時刪除 log 包導入聲明, 否則, 編譯器將會發一個錯誤. 在這種情況下, 我們需要將不必要的導入刪除或註釋掉.
如果導入一個包, 但是沒有使用該包將被當作一個錯誤. 這種強製檢測可以有效減少不必要的依賴, 雖然在調試期間會讓人討厭, 因爲刪除一個類似 log.Print("got here!") 的打印可能導致需要同時刪除 log 包導入聲明, 否則, 編譯器將會發一個錯誤. 在這種情況下, 我們需要將不必要的導入刪除或註釋掉.
不過有更好的解決方案, 我們可以使用 golang.org/x/tools/cmd/goimports 工具, 它可以根據需要自動添加或刪除導入的包; 許多編輯器都可以集成 goimports 工具, 然後在保存文件的時候自動允許它. 類似的還有 gofmt 工具, 可以用來格式化Go源文件.

View File

@@ -1,6 +1,6 @@
### 2.6.2. 包的初始化
包的初始化首先是解決包級變量的依賴順序, 然後安裝包級變量聲明現的順序依次初始化:
包的初始化首先是解決包級變量的依賴順序, 然後安裝包級變量聲明現的順序依次初始化:
```Go
var a = b + c // a 第三個初始化, 爲 3

View File

@@ -4,7 +4,7 @@ Go語言中的包和其他語言的庫或模塊概念類似, 目的都是爲了
每個包作爲一個獨立的名字空間. 例如, 在 image 包中的 Decode 函數 和 unicode/utf16 包中的 Decode 函數是不同的. 要在外部包引用該函數, 必鬚顯式使用 image.Decode 或 utf16.Decode 訪問.
包可以讓我們通過控製那些名字是外部可見的來隱藏信息. 在Go中, 一個簡單的規則是: 如果一個名字是大寫字母開頭的, 那麽該名字是導的.
包可以讓我們通過控製那些名字是外部可見的來隱藏信息. 在Go中, 一個簡單的規則是: 如果一個名字是大寫字母開頭的, 那麽該名字是導的.
爲了演示基本的用法, 假設我們的溫度轉換軟件已經很流行, 我們希望到Go社區也能使用這個包. 我們該如何做呢?

View File

@@ -112,7 +112,7 @@ if f, err := os.Open(fname); err != nil {
但這不是Go推薦的做法, Go的習慣是在if中處理錯誤然後直接返迴, 這樣可以確保正常成功執行的語句不需要代碼縮進.
要特别註意短的變量聲明的作用域范圍, 考慮下面的程序, 它的目的是穫取當前的工作目録然後保存到一個包級的變量中. 這可以通過直接調用 os.Getwd 完成, 但是將這個從主邏輯中分離來可能會更好, 特别是在需要處理錯誤的時候. 函數 log.Fatalf 打印信息, 然後調用 os.Exit(1) 終止程序.
要特别註意短的變量聲明的作用域范圍, 考慮下面的程序, 它的目的是穫取當前的工作目録然後保存到一個包級的變量中. 這可以通過直接調用 os.Getwd 完成, 但是將這個從主邏輯中分離來可能會更好, 特别是在需要處理錯誤的時候. 函數 log.Fatalf 打印信息, 然後調用 os.Exit(1) 終止程序.
```Go
var cwd string
@@ -141,9 +141,9 @@ func init() {
}
```
全局的cwd變量依然是沒有被正確初始化的, 而且看似正常的日誌輸更是這個BUG更加隱晦.
全局的cwd變量依然是沒有被正確初始化的, 而且看似正常的日誌輸更是這個BUG更加隱晦.
有許多方式可以避免現類似潛在的問題. 最直接的是通過單獨聲明err變量, 來避免使用 `:=` 的簡短聲明方式:
有許多方式可以避免現類似潛在的問題. 最直接的是通過單獨聲明err變量, 來避免使用 `:=` 的簡短聲明方式:
```Go
var cwd string