## 1.1. Hello, World
我們以現已成為傳統的“hello world”案例來開始吧, 這個例子首次出現于1978年出版的C語言圣經[《The C Programming Language》](http://s3-us-west-2.amazonaws.com/belllabs-microsite-dritchie/cbook/index.html)(譯注:本書作者之一Brian W. Kernighan也是《The C Programming Language》一書的作者)。C語言是直接影響Go語言設計的語言之一。這個例子體現了Go語言一些核心理念。
<u><i>gopl.io/ch1/helloworld</i></u>
```go
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}
```
Go是一門編譯型語言,Go語言的工具鏈將源代碼及其依賴轉換成計算機的機器指令(譯注:靜態編譯)。Go語言提供的工具都通過一個單獨的命令`go`調用,`go`命令有一系列子命令。最簡單的一個子命令就是run。這個命令編譯一個或多個以.go結尾的源文件,鏈接庫文件,并運行最終生成的可執行文件。(本書使用$表示命令行提示符。)
```
$ go run helloworld.go
```
毫無意外,這個命令會輸出:
```
Hello, 世界
```
Go語言原生支持Unicode,它可以處理全世界任何語言的文本。
如果不只是一次性實驗,你肯定希望能夠編譯這個程序,保存編譯結果以備將來之用。可以用build子命令:
```
$ go build helloworld.go
```
這個命令生成一個名為helloworld的可執行的二進制文件(譯注:Windows系統下生成的可執行文件是helloworld.exe,增加了.exe后綴名),之后你可以隨時運行它(譯注:在Windows系統下在命令行直接輸入helloworld.exe命令運行),不需任何處理(譯注:因為靜態編譯,所以不用擔心在系統庫更新的時候沖突,幸福感滿滿)。
```
$ ./helloworld
Hello, 世界
```
本書中, 所有的示例代碼上都有一行標記,利用這些標記, 可以從[gopl.io](http://gopl.io)網站上本書源碼倉庫里獲取代碼:
```
gopl.io/ch1/helloworld
```
執行 `go get gopl.io/ch1/helloworld` 命令,就會從網上獲取代碼,并放到對應目錄中(需要先安裝Git或Hg之類的版本管理工具,并將對應的命令添加到PATH環境變量中。序言已經提及,需要先設置好GOPATH環境變量,下載的代碼會放在`$GOPATH/src/gopl.io/ch1/helloworld`目錄)。2.6和10.7節有這方面更詳細的介紹。
來討論下程序本身。Go語言的代碼通過**包**(package)組織,包類似于其它語言里的庫(libraries)或者模塊(modules)。一個包由位于單個目錄下的一個或多個.go源代碼文件組成, 目錄定義包的作用。每個源文件都以一條`package`聲明語句開始,這個例子里就是`package main`, 表示該文件屬于哪個包,緊跟著一系列導入(import)的包,之后是存儲在這個文件里的程序語句。
Go的標準庫提供了100多個包,以支持常見功能,如輸入、輸出、排序以及文本處理。比如`fmt`包,就含有格式化輸出、接收輸入的函數。`Println`是其中一個基礎函數,可以打印以空格間隔的一個或多個值,并在最后添加一個換行符,從而輸出一整行。
`main`包比較特殊。它定義了一個獨立可執行的程序,而不是一個庫。在`main`里的`main` *函數* 也很特殊,它是整個程序執行時的入口(譯注:C系語言差不多都這樣)。`main`函數所做的事情就是程序做的。當然了,`main`函數一般調用其它包里的函數完成很多工作, 比如, `fmt.Println`。
必須告訴編譯器源文件需要哪些包,這就是跟隨在`package`聲明后面的`import`聲明扮演的角色。hello world例子只用到了一個包,大多數程序需要導入多個包。
必須恰當導入需要的包,缺少了必要的包或者導入了不需要的包,程序都無法編譯通過。這項嚴格要求避免了程序開發過程中引入未使用的包(譯注:Go語言編譯過程沒有警告信息,爭議特性之一)。
`import`聲明必須跟在文件的`package`聲明之后。隨后,則是組成程序的函數、變量、常量、類型的聲明語句(分別由關鍵字`func`, `var`, `const`, `type`定義)。這些內容的聲明順序并不重要(譯注:最好還是定一下規范)。這個例子的程序已經盡可能短了,只聲明了一個函數, 其中只調用了一個其他函數。為了節省篇幅,有些時候, 示例程序會省略`package`和`import`聲明,但是,這些聲明在源代碼里有,并且必須得有才能編譯。
一個函數的聲明由`func`關鍵字、函數名、參數列表、返回值列表(這個例子里的`main`函數參數列表和返回值都是空的)以及包含在大括號里的函數體組成。第五章進一步考察函數。
Go語言不需要在語句或者聲明的末尾添加分號,除非一行上有多條語句。實際上,編譯器會主動把特定符號后的換行符轉換為分號, 因此換行符添加的位置會影響Go代碼的正確解析(譯注:比如行末是標識符、整數、浮點數、虛數、字符或字符串文字、關鍵字`break`、`continue`、`fallthrough`或`return`中的一個、運算符和分隔符`++`、`--`、`)`、`]`或`}`中的一個)。舉個例子, 函數的左括號`{`必須和`func`函數聲明在同一行上, 且位于末尾,不能獨占一行,而在表達式`x + y`中,可在`+`后換行,不能在`+`前換行(譯注:以+結尾的話不會被插入分號分隔符,但是以x結尾的話則會被分號分隔符,從而導致編譯錯誤)。
Go語言在代碼格式上采取了很強硬的態度。`gofmt`工具把代碼格式化為標準格式(譯注:這個格式化工具沒有任何可以調整代碼格式的參數,Go語言就是這么任性),并且`go`工具中的`fmt`子命令會對指定包, 否則默認為當前目錄, 中所有.go源文件應用`gofmt`命令。本書中的所有代碼都被gofmt過。你也應該養成格式化自己的代碼的習慣。以法令方式規定標準的代碼格式可以避免無盡的無意義的瑣碎爭執(譯注:也導致了Go語言的TIOBE排名較低,因為缺少撕逼的話題)。更重要的是,這樣可以做多種自動源碼轉換,如果放任Go語言代碼格式,這些轉換就不大可能了。
很多文本編輯器都可以配置為保存文件時自動執行`gofmt`,這樣你的源代碼總會被恰當地格式化。還有個相關的工具,`goimports`,可以根據代碼需要, 自動地添加或刪除`import`聲明。這個工具并沒有包含在標準的分發包中,可以用下面的命令安裝:
```
$ go get golang.org/x/tools/cmd/goimports
```
對于大多數用戶來說,下載、編譯包、運行測試用例、察看Go語言的文檔等等常用功能都可以用go的工具完成。10.7節詳細介紹這些知識。
- 前言
- Go語言起源
- Go語言項目
- 本書的組織
- 更多的信息
- 致謝
- 入門
- Hello, World
- 命令行參數
- 查找重復的行
- GIF動畫
- 獲取URL
- 并發獲取多個URL
- Web服務
- 本章要點
- 程序結構
- 命名
- 聲明
- 變量
- 賦值
- 類型
- 包和文件
- 作用域
- 基礎數據類型
- 整型
- 浮點數
- 復數
- 布爾型
- 字符串
- 常量
- 復合數據類型
- 數組
- Slice
- Map
- 結構體
- JSON
- 文本和HTML模板
- 函數
- 函數聲明
- 遞歸
- 多返回值
- 錯誤
- 函數值
- 匿名函數
- 可變參數
- Deferred函數
- Panic異常
- Recover捕獲異常
- 方法
- 方法聲明
- 基于指針對象的方法
- 通過嵌入結構體來擴展類型
- 方法值和方法表達式
- 示例: Bit數組
- 封裝
- 接口
- 接口是合約
- 接口類型
- 實現接口的條件
- flag.Value接口
- 接口值
- sort.Interface接口
- http.Handler接口
- error接口
- 示例: 表達式求值
- 類型斷言
- 基于類型斷言識別錯誤類型
- 通過類型斷言查詢接口
- 類型分支
- 示例: 基于標記的XML解碼
- 補充幾點
- Goroutines和Channels
- Goroutines
- 示例: 并發的Clock服務
- 示例: 并發的Echo服務
- Channels
- 并發的循環
- 示例: 并發的Web爬蟲
- 基于select的多路復用
- 并發的退出
- 示例: 聊天服務
- 基于共享變量的并發
- 競爭條件
- sync.Mutex互斥鎖
- sync.RWMutex讀寫鎖
- 內存同步
- 競爭條件檢測
- 示例: 并發的非阻塞緩存
- Goroutines和線程
- 包和工具
- 包簡介
- 導入路徑
- 包聲明
- 導入聲明
- 包的匿名導入
- 包和命名
- 工具
- 測試
- go test
- 測試函數
- 測試覆蓋率
- 基準測試
- 剖析
- 示例函數
- 反射
- 為何需要反射?
- reflect.Type和reflect.Value
- Display遞歸打印
- 示例: 編碼S表達式
- 通過reflect.Value修改值
- 示例: 解碼S表達式
- 顯示一個類型的方法集
- 幾點忠告
- 底層編程
- unsafe.Sizeof, Alignof 和 Offsetof
- unsafe.Pointer
- 示例: 深度相等判斷
- 通過cgo調用C代碼
- 幾點忠告
- 附錄
- 附錄A:原文勘誤
- 附錄B:作者譯者
- 附錄C:譯文授權
- 附錄D:其它語言