[TOC]
## 1.2.1 影響 Go 語言發展的早期編程語言
正如 “21 世紀的 C 語言” 這句話所說,Go 語言并不是憑空而造的,而是和 C++、Java 和 C# 一樣屬于 C 系。不僅如此,設計者們還汲取了其它編程語言的精粹部分融入到 Go 語言當中。
在聲明和包的設計方面,Go 語言受到 Pascal、Modula 和 Oberon 系語言的影響;在并發原理的設計上,Go 語言從同樣受到 Tony Hoare 的 CSP(通信序列進程?*Communicating Squential Processes*)理論影響的 Limbo 和 Newsqueak 的實踐中借鑒了一些經驗,并使用了和 Erlang 類似的機制。
這是一門完全開源的編程語言,因為它使用 BSD 授權許可,所以任何人都可以進行商業軟件的開發而不需要支付任何費用。
盡管為了能夠讓目前主流的開發者們能夠對 Go 語言中的類 C 語言的語法感到非常親切而易于轉型,但是它在極大程度上簡化了這些語法,使得它們比 C/C++ 的語法更加簡潔和干凈。同時,Go 語言也擁有一些動態語言的特性,這使得使用 Python 和 Ruby 的開發者們在使用 Go 語言的時候感覺非常容易上手。
下圖展示了一些其它編程語言對 Go 語言的影響:

圖 1.3 其它編程語言對 Go 語言的影響
## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.2.md#122-為什么要創造一門編程語言)1.2.2 為什么要創造一門編程語言
* C/C++ 的發展速度無法跟上計算機發展的腳步,十多年來也沒有出現一門與時代相符的主流系統編程語言,因此人們需要一門新的系統編程語言來彌補這個空缺,尤其是在計算機信息時代。
* 對比計算機性能的提升,軟件開發領域不被認為發展地足夠快或者比硬件發展更加成功(有許多項目均以失敗告終),同時應用程序的體積始終在不斷地擴大,這就迫切地需要一門具備更高層次概念的低級語言來突破現狀。
* 在 Go 語言出現之前,開發者們總是面臨非常艱難的抉擇,究竟是使用執行速度快但是編譯速度并不理想的語言(如:C++),還是使用編譯速度較快但執行效率不佳的語言(如:.NET、Java),或者說開發難度較低但執行速度一般的動態語言呢?顯然,Go 語言在這 3 個條件之間做到了最佳的平衡:快速編譯,高效執行,易于開發。
## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.2.md#123-go-語言的發展目標)1.2.3 Go 語言的發展目標
Go 語言的主要目標是將靜態語言的安全性和高效性與動態語言的易開發性進行有機結合,達到完美平衡,從而使編程變得更加有樂趣,而不是在艱難抉擇中痛苦前行。
因此,Go 語言是一門類型安全和內存安全的編程語言。雖然 Go 語言中仍有指針的存在,但并不允許進行指針運算。
Go 語言的另一個目標是對于網絡通信、并發和并行編程的極佳支持,從而更好地利用大量的分布式和多核的計算機,這一點對于谷歌內部的使用來說就非常重要了。設計者通過 goroutine 這種輕量級線程的概念來實現這個目標,然后通過 channel 來實現各個 goroutine 之間的通信。他們實現了分段棧增長和 goroutine 在線程基礎上多路復用技術的自動化。
這個特性顯然是 Go 語言最強有力的部分,不僅支持了日益重要的多核與多處理器計算機,也彌補了現存編程語言在這方面所存在的不足。
Go 語言中另一個非常重要的特性就是它的構建速度(編譯和鏈接到機器代碼的速度),一般情況下構建一個程序的時間只需要數百毫秒到幾秒。作為大量使用 C++ 來構建基礎設施的谷歌來說,無疑從根本上擺脫了 C++ 在構建速度上非常不理想的噩夢。這不僅極大地提升了開發者的生產力,同時也使得軟件開發過程中的代碼測試環節更加緊湊,而不必浪費大量的時間在等待程序的構建上。
依賴管理是現今軟件開發的一個重要組成部分,但是 C 語言中“頭文件”的概念卻導致越來越多因為依賴關系而使得構建一個大型的項目需要長達幾個小時的時間。人們越來越需要一門具有嚴格的、簡潔的依賴關系分析系統從而能夠快速編譯的編程語言。這正是 Go 語言采用包模型的根本原因,這個模型通過嚴格的依賴關系檢查機制來加快程序構建的速度,提供了非常好的可量測性。
整個 Go 語言標準庫的編譯時間一般都在 20 秒以內,其它的常規項目也只需要半秒鐘的時間來完成編譯工作。這種閃電般的編譯速度甚至比編譯 C 語言或者 Fortran 更加快,使得編譯這一環節不再成為在軟件開發中困擾開發人員的問題。在這之前,動態語言將快速編譯作為自身的一大亮點,像 C++ 那樣的靜態語言一般都有非常漫長的編譯和鏈接工作。而同樣作為靜態語言的 Go 語言,通過自身優良的構建機制,成功地了去除了這個弊端,使得程序的構建過程變得微不足道,擁有了像腳本語言和動態語言那樣的高效開發的能力。
另外,Go 語言在執行速度方面也可以與 C/C++ 相提并論。
由于內存問題(通常稱為內存泄漏)長期以來一直伴隨著 C++ 的開發者們,Go 語言的設計者們認為內存管理不應該是開發人員所需要考慮的問題。因此盡管 Go 語言像其它靜態語言一樣執行本地代碼,但它依舊運行在某種意義上的虛擬機,以此來實現高效快速的垃圾回收(使用了一個簡單的標記-清除算法)。
盡管垃圾回收并不容易實現,但考慮這將是未來并發應用程序發展的一個重要組成部分,Go 語言的設計者們還是完成了這項艱難的任務。
Go 語言還能夠在運行時進行反射相關的操作。
使用?`go install`?能夠很輕松地對第三方包進行部署。
此外,Go 語言還支持調用由 C 語言編寫的海量庫文件(第 3.9 節),從而能夠將過去開發的軟件進行快速遷移。
## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.2.md#124-指導設計原則)1.2.4 指導設計原則
Go語言通過減少關鍵字的數量(25 個)來簡化編碼過程中的混亂和復雜度。干凈、整齊和簡潔的語法也能夠提高程序的編譯速度,因為這些關鍵字在編譯過程中少到甚至不需要符號表來協助解析。
這些方面的工作都是為了減少編碼的工作量,甚至可以與 Java 的簡化程度相比較。
Go 語言有一種極簡抽象藝術家的感覺,因為它只提供了一到兩種方法來解決某個問題,這使得開發者們的代碼都非常容易閱讀和理解。眾所周知,代碼的可讀性是軟件工程里最重要的一部分(?**譯者注:代碼是寫給人看的,不是寫給機器看的**)。
這些設計理念沒有建立其它概念之上,所以并不會因為牽扯到一些概念而將某個概念復雜化,他們之間是相互獨立的。
Go 語言有一套完整的編碼規范,你可以在?[Go 語言編碼規范](http://golang.org/doc/go_spec.html)?頁面進行查看。
它不像 Ruby 那樣通過實現過程來定義編碼規范。作為一門具有明確編碼規范的語言,它要求可以采用不同的編譯器如 gc 和 gccgo(第 2.1 節)進行編譯工作,這對語言本身擁有更好的編碼規范起到很大幫助。
[LALR](http://en.wikipedia.org/wiki/LALR_parser)?是 Go 語言的語法標準,你也可以在?[`src/cmd/internal/gc/go.y`](https://github.com/golang/go/blob/master/src%2Fcmd%2Finternal%2Fgc%2Fgo.y)?中查看到,這種語法標準在編譯時不需要符號表來協助解析。
## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.2.md#125-語言的特性)1.2.5 語言的特性
Go 語言從本質上(程序和結構方面)來實現并發編程。
因為 Go 語言沒有類和繼承的概念,所以它和 Java 或 C++ 看起來并不相同。但是它通過接口(interface)的概念來實現多態性。Go 語言有一個清晰易懂的輕量級類型系統,在類型之間也沒有層級之說。因此可以說這是一門混合型的語言。
在傳統的面向對象語言中,使用面向對象編程技術顯得非常的臃腫,它們總是通過復雜的模式來構建龐大的類型層級,這違背了編程語言應該提升生產力的宗旨。
函數是 Go 語言中的基本構件,它們的使用方法非常靈活。在第六章,我們會看到 Go 語言在函數式編程方面的基本概念。
Go 語言使用靜態類型,所以它是類型安全的一門語言,加上通過構建到本地代碼,程序的執行速度也非常快。
作為強類型語言,隱式的類型轉換是不被允許的,記住一條原則:讓所有的東西都是顯式的。
Go 語言其實也有一些動態語言的特性(通過關鍵字?`var`),所以它對那些逃離 Java 和 .Net 世界而使用 Python、Ruby、PHP 和 JavaScript 的開發者們也具有很大的吸引力。
Go 語言支持交叉編譯,比如說你可以在運行 Linux 系統的計算機上開發運行下 Windows 下運行的應用程序。這是第一門完全支持 UTF-8 的編程語言,這不僅體現在它可以處理使用 UTF-8 編碼的字符串,就連它的源碼文件格式都是使用的 UTF-8 編碼。Go 語言做到了真正的國際化!
## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.2.md#126-語言的用途)1.2.6 語言的用途
Go 語言被設計成一門應用于搭載 Web 服務器,存儲集群或類似用途的巨型中央服務器的系統編程語言。對于高性能分布式系統領域而言,Go 語言無疑比大多數其它語言有著更高的開發效率。它提供了海量并行的支持,這對于游戲服務端的開發而言是再好不過了。
Go 語言一個非常好的目標就是實現所謂的復雜事件處理([CEP](http://en.wikipedia.org/wiki/Complex_event_processing)),這項技術要求海量并行支持,高度的抽象化和高性能。當我們進入到物聯網時代,CEP 必然會成為人們關注的焦點。
但是 Go 語言同時也是一門可以用于實現一般目標的語言,例如對于文本的處理,前端展現,甚至像使用腳本一樣使用它。
值得注意的是,因為垃圾回收和自動內存分配的原因,Go 語言不適合用來開發對實時性要求很高的軟件。
越來越多的谷歌內部的大型分布式應用程序都開始使用 Go 語言來開發,例如谷歌地球的一部分代碼就是由 Go 語言完成的。
如果你想知道一些其它組織使用Go語言開發的實際應用項目,你可以到?[使用 Go 的組織](http://go-lang.cat-v.org/organizations-using-go)?頁面進行查看。出于隱私保護的考慮,許多公司的項目都沒有展示在這個頁面。我們將會在第 21 章討論到一個使用 Go 語言開發的大型存儲區域網絡(SAN)案例。
在 Chrome 瀏覽器中內置了一款 Go 語言的編譯器用于本地客戶端(NaCl),這很可能會被用于在 Chrome OS 中執行 Go 語言開發的應用程序。
Go 語言可以在 Intel 或 ARM 處理器上運行,因此它也可以在安卓系統下運行,例如 Nexus 系列的產品。
在 Google App Engine 中使用 Go 語言:2011 年 5 月 5 日,官方發布了用于開發運行在 Google App Engine 上的 Web 應用的 Go SDK,在此之前,開發者們只能選擇使用 Python 或者 Java。這主要是 David Symonds 和 Nigel Tao 努力的成果。目前最新的穩定版是基于 Go 1.4 的 SDK 1.9.18,于 2015 年 2 月 18 日發布。當前 Go 語言的穩定版本是 Go 1.4.2。
## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.2.md#127-關于特性缺失)1.2.7 關于特性缺失
許多能夠在大多數面向對象語言中使用的特性 Go 語言都沒有支持,但其中的一部分可能會在未來被支持。
* 為了簡化設計,不支持函數重載和操作符重載
* 為了避免在 C/C++ 開發中的一些 Bug 和混亂,不支持隱式轉換
* Go 語言通過另一種途徑實現面向對象設計(第 10-11 章)來放棄類和類型的繼承
* 盡管在接口的使用方面(第 11 章)可以實現類似變體類型的功能,但本身不支持變體類型
* 不支持動態加載代碼
* 不支持動態鏈接庫
* 不支持泛型
* 通過?`recover`?和?`panic`?來替代異常機制(第 13.2-3 節)
* 不支持斷言
* 不支持靜態變量
關于 Go 語言開發團隊對于這些方面的討論,你可以通過?[Go 常見問題](http://golang.org/doc/go_faq.html)?頁面查看。
## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.2.md#128-使用-go-語言編程)1.2.8 使用 Go 語言編程
如果你有其它語言的編程經歷(面向對象編程語言,如:Java、C#、Object-C、Python、Ruby),在你進入到 Go 語言的世界之后,你將會像迷戀你的 X 語言一樣無法自拔。Go 語言使用了與其它語言不同的設計模式,所以當你嘗試將你的X語言的代碼遷移到 Go 語言時,你將會非常失望,所以你需要從頭開始,用 Go 的理念來思考。
如果你在至高點使用 Go 的理念來重新審視和分析一個問題,你通常會找到一個適用于 Go 語言的優雅的解決方案。
## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.2.md#129-小結)1.2.9 小結
這里列舉一些 Go 語言的必殺技:
* 簡化問題,易于學習
* 內存管理,簡潔語法,易于使用
* 快速編譯,高效開發
* 高效執行
* 并發支持,輕松駕馭
* 靜態類型
* 標準類庫,規范統一
* 易于部署
* 文檔全面
* 免費開源
- 前言
- 第一部分:學習 Go 語言
- 第1章:Go 語言的起源,發展與普及
- 1.1 起源與發展
- 1.2 語言的主要特性與發展的環境和影響因素
- 第2章:安裝與運行環境
- 2.1 平臺與架構
- 2.2 Go 環境變量
- 2.3 在 Linux 上安裝 Go
- 2.4 在 Mac OS X 上安裝 Go
- 2.5 在 Windows 上安裝 Go
- 2.6 安裝目錄清單
- 2.7 Go 運行時(runtime)
- 2.8 Go 解釋器
- 第3章:編輯器、集成開發環境與其它工具
- 3.1 Go 開發環境的基本要求
- 3.2 編輯器和集成開發環境
- 3.3 調試器
- 3.4 構建并運行 Go 程序
- 3.5 格式化代碼
- 3.6 生成代碼文檔
- 3.7 其它工具
- 3.8 Go 性能說明
- 3.9 與其它語言進行交互
- 第二部分:語言的核心結構與技術
- 第4章:基本結構和基本數據類型
- 4.1 文件名、關鍵字與標識符
- 4.2 Go 程序的基本結構和要素
- 4.3 常量
- 4.4 變量
- 4.5 基本類型和運算符
- 4.6 字符串
- 4.7 strings 和 strconv 包
- 4.8 時間和日期
- 4.9 指針
- 第5章:控制結構
- 5.1 if-else 結構
- 5.2 測試多返回值函數的錯誤
- 5.3 switch 結構
- 5.4 for 結構
- 5.5 Break 與 continue
- 5.6 標簽與 goto
- 第6章:函數(function)
- 6.1 介紹
- 6.2 函數參數與返回值
- 6.3 傳遞變長參數
- 6.4 defer 和追蹤
- 6.5 內置函數
- 6.6 遞歸函數
- 6.7 將函數作為參數
- 6.8 閉包
- 6.9 應用閉包:將函數作為返回值
- 6.10 使用閉包調試
- 6.11 計算函數執行時間
- 6.12 通過內存緩存來提升性能
- 第7章:數組與切片
- 7.1 聲明和初始化
- 7.2 切片
- 7.3 For-range 結構
- 7.4 切片重組(reslice)
- 7.5 切片的復制與追加
- 7.6 字符串、數組和切片的應用
- 第8章:Map
- 8.1 聲明、初始化和 make
- 8.2 測試鍵值對是否存在及刪除元素
- 8.3 for-range 的配套用法
- 8.4 map 類型的切片
- 8.5 map 的排序
- 8.6 將 map 的鍵值對調
- 第9章:包(package)
- 9.1 標準庫概述
- 9.2 regexp 包
- 9.3 鎖和 sync 包
- 9.4 精密計算和 big 包
- 9.5 自定義包和可見性
- 9.6 為自定義包使用 godoc
- 9.7 使用 go install 安裝自定義包
- 9.8 自定義包的目錄結構、go install 和 go test
- 9.9 通過 Git 打包和安裝
- 9.10 Go 的外部包和項目
- 9.11 在 Go 程序中使用外部庫
- 第10章:結構(struct)與方法(method)
- 10.1 結構體定義
- 10.2 使用工廠方法創建結構體實例
- 10.3 使用自定義包中的結構體
- 10.4 帶標簽的結構體
- 10.5 匿名字段和內嵌結構體
- 10.6 方法
- 10.8 垃圾回收和 SetFinalizer
- 第11章:接口(interface)與反射(reflection)
- 11.1 接口是什么
- 11.2 接口嵌套接口
- 11.3 類型斷言:如何檢測和轉換接口變量的類型
- 11.4 類型判斷:type-switch
- 11.5 測試一個值是否實現了某個接口
- 11.6 使用方法集與接口
- 11.7 第一個例子:使用 Sorter 接口排序
- 11.8 第二個例子:讀和寫
- 11.9 空接口
- 11.10 反射包
- 第三部分:Go 高級編程
- 第12章 讀寫數據
- 12.1 讀取用戶的輸入
- 12.2 文件讀寫
- 12.3 文件拷貝
- 12.4 從命令行讀取參數
- 12.5 用buffer讀取文件
- 12.6 用切片讀寫文件
- 12.7 用 defer 關閉文件
- 12.8 使用接口的實際例子:fmt.Fprintf
- 12.9 Json 數據格式
- 12.10 XML 數據格式
- 12.11 用 Gob 傳輸數據
- 12.12 Go 中的密碼學
- 第13章 錯誤處理與測試
- 13.1 錯誤處理
- 13.2 運行時異常和 panic
- 13.3 從 panic 中恢復(Recover)
- 13.4 自定義包中的錯誤處理和 panicking
- 13.5 一種用閉包處理錯誤的模式
- 13.6 啟動外部命令和程序
- 13.7 Go 中的單元測試和基準測試
- 13.8 測試的具體例子
- 13.9 用(測試數據)表驅動測試
- 13.10 性能調試:分析并優化 Go 程序
- 第14章:協程(goroutine)與通道(channel)
- 14.1 并發、并行和協程
- 14.2 使用通道進行協程間通信
- 14.3 協程同步:關閉通道-對阻塞的通道進行測試
- 14.4 使用 select 切換協程
- 14.5 通道,超時和計時器(Ticker)
- 14.6 協程和恢復(recover)
- 第15章:網絡、模版與網頁應用
- 15.1 tcp服務器
- 15.2 一個簡單的web服務器
- 15.3 訪問并讀取頁面數據
- 15.4 寫一個簡單的網頁應用
- 第四部分:實際應用
- 第16章:常見的陷阱與錯誤
- 16.1 誤用短聲明導致變量覆蓋
- 16.2 誤用字符串
- 16.3 發生錯誤時使用defer關閉一個文件
- 16.5 不需要將一個指向切片的指針傳遞給函數
- 16.6 使用指針指向接口類型
- 16.7 使用值類型時誤用指針
- 16.8 誤用協程和通道
- 16.9 閉包和協程的使用
- 16.10 糟糕的錯誤處理
- 第17章:模式
- 17.1 關于逗號ok模式
- 第18章:出于性能考慮的實用代碼片段
- 18.1 字符串
- 18.2 數組和切片
- 18.3 映射
- 18.4 結構體
- 18.5 接口
- 18.6 函數
- 18.7 文件
- 18.8 協程(goroutine)與通道(channel)
- 18.9 網絡和網頁應用
- 18.10 其他
- 18.11 出于性能考慮的最佳實踐和建議
- 附錄