Go 語言手冊
Golang 是一門非常棒的簡潔且快速的現代編程語言。
它是編譯的、開源的、強類型的。
Golang——也被稱爲 Go——由谷歌工程師創建,主要目標如下:
-
讓他們的項目編譯(和運行)更快
-
要簡單,這樣人們可以在短時間內學會
-
要足夠底層,但也要避免過於關注底層
-
可移植(經過編譯的 Go 程序不需要其他文件的支持,就可以跨平臺運行,因此它們很輕易分發)
-
簡潔而枯燥、穩定、可預測,從而減少犯錯的機會
-
易於利用多處理器系統
Go 是爲了取代 C 和 C++ 代碼庫。它旨在通過垃圾回收機制使併發和內存管理等事情變得更簡單。
此外,由於其與 C 語言的互操作特性,可以在 C 和 C++ 代碼庫中使用 Go 語言。
你可以用 Go 做很多不一樣的任務,它既可以解決簡單的問題也可以解決複雜的問題。
你可以使用 Go 來創建命令行工具和網絡服務器,它廣泛用於許多不同的場景。
舉個例子,Docker 和 K8s 是用 Go 編寫的。
我最喜歡的靜態網站生成工具 Hugo 是用 Go 編寫的。
Caddy,一個非常流行的 web 服務器是用 GO 編寫的。
衆多被廣泛使用的工具都是基於這門語言創建的。
本手冊將向你介紹 Go 編程語言,以便你開始使用 Go 編程。
你可以點擊鏈接獲得 GO 初學者手冊的 pdf 版本和 ePub 版本。
目錄
-
如何開始使用 Go
-
如何安裝 Go
-
如何設置編輯器
-
如何用 Go 編寫 Hello, World!
-
如何編譯和運行 Go 程序
-
Go 的工作空間
-
深入 Go 語言
-
Go 中的變量
-
Go 的基礎類型
-
Go 中的字符串
-
Go 中的數組
-
Go 中的切片
-
Go 中的 map
-
Go 中的循環
-
Go 中的條件語句
-
Go 中的運算符
-
Go 中的結構體
-
Go 中的函數
-
Go 中的指針
-
Go 中的方法
-
Go 中的接口
-
更多內容
如何開始使用 Go
在我們深入瞭解語言的細節之前,你應該瞭解以下幾點。
首先, https://go.dev 是語言的官網。在官網你可以獲得以下資源:
-
下載 Go 的二進制文件(
go命令和其他相關工具)https://go.dev/doc/install -
參考 Go 的官方文檔 https://go.dev/doc/
-
查看 Go 的所有第三方庫 https://pkg.go.dev/
-
進入 Go 遊樂園 https://go.dev/play/
...... 等等。
如何安裝 Go
去 https://go.dev/doc/install 下載適用於你電腦操作系統的軟件包。
運行並安裝,在最後一個步驟你需要在命令行設置 go 命令:
歡迎進行 Go 的安裝
安裝成功
打開命令行並運行 go version 你會看到以下內容:
展示你當前的 Go 版本
注意:在運行程序之前,你可能需要打開一個新的命令行,因爲安裝程序將 Go 二進制文件文件夾添加到了路徑中。
Go 安裝文件的確切位置取決於你的操作系統。
在 Mac 系統中,它在 /usr/local/go , 運行文件在 /usr/local/go/bin 。
在 Windows 系統中,它在 C:\Program Files\go。
在 Windows 和 Mac 安裝中 Go 執行文件路徑都是自動設定的。
在 Mac 你可以在 Homebrew 中使用 brew install golang 命令安裝。這樣方式更容易升級到最新版本。
在 Linux 上,你必須將 Go 二進制文件文件夾添加到你的環境變量中,然後才能在使用以下命令將 Linux 包解壓縮到 /usr/local/go之後運行 go 命令:
echo 'export PATH=$PATH:/usr/local/go/bin' >> $HOME/.profile
source $HOME/.profile
如何設置編輯器
我推薦使用 Visual Studio Code(也叫 VS Code)作爲你的編輯器。
請閱讀在 Visual Studio Code 的 Go,瞭解快速安裝並運行的方法。安裝 Go 的擴展。
VS Code 中的 Go 擴展
這個擴展將讓你的生活更加輕鬆。因爲它提供智能感知(語法高亮顯示、自動補全、懸停信息、錯誤高亮顯示…)和其他功能,如自動格式化、安裝軟件包的菜單選項、測試等。
如何用 Go 編寫 Hello, World!
現在我們準備創建我們第一個 Go 程序!
程序員的傳統是讓第一個程序打印 “Hello, World!” 字符串到命令行。所以我們先做這個,然後解釋我們是如何做到的。
或許你可以在你主目錄下創建一個文件夾, 保存你所有編寫的項目和測試。
在這,創建一個新的文件夾,比如取名叫 hello。
在這,創建一個叫 hello.go 的文件(你可以用任何想要用的名字)。
文件內容如下:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Go "Hello, World!" 代碼
這是你編寫的第一個 Go 程序!
讓我們逐行分析一下。
package main
我們以包的形式組織 Go 程序。
每個.go 文件首先要聲明它是哪個包的一部分。
一個包可以由多個文件組成,也可以僅由一個文件組成。
一個程序可以由多個包組成。
這個 main 是程序識別可執行程序的入口。
import "fmt"
我們使用 import 關鍵字導入包。
fmt 是 Go 提供的內置包,提供輸入 / 輸出的工具函數。
我們有一個大的標準庫,可以隨時使用,從網絡連接到數學、加密、圖像處理、文件系統訪問等等。
你可以在官方文檔閱讀 fmt 包提供的所有功能。
func main() {
}
這裏我們聲明 main() 函數。
什麼是函數?稍後我們將看到更多關於它們的信息,但同時讓我們假設一個函數是一個分配了名稱的代碼塊,包含一些指令。
main 函數是特殊的,因爲這也是程序啓動的地方。
在這個簡單的例子中,我們只是有一個函數——程序從這個函數開始,然後結束。
fmt.Println("Hello, World!")
這是我們定義的函數的內容。
我們調用了我們之前導入的 fmt 包中 Println() 函數,將字符串作爲參數傳遞。
根據文檔,“根據格式指定器進行格式化並寫入標準輸出”。
看看這些文檔,因爲它們很棒。它們甚至有你可以運行的示例:
Go 基礎的函數模版
我們可以用 “dot” 語法 fmt.Println() 指定該函數由該包提供。
代碼在執行 main 函數之後,它沒有做其他的事就結束了執行。
如何編譯和運行 Go 程序
現在在 hello 文件夾下打開命令行,用以下命令運行程序:
go run hello.go
Go 輸出 Hello World
我們的程序運行成功,它會在命令行輸出 “Hello, World!” 。
go run 會先編譯並運行程序。
你可以用 go build 創建一個可執行文件:
go build hello.go
這裏會創建一個名爲 hello 的可執行文件,你可以執行:
執行 Go 的可執行文件
在引言部分我提到過 Go 是可移植的。
現在你可以分發這個二進制文件,每個人都可以按原樣運行程序,因爲二進制文件已經打包好了,可以執行了。
該程序將在我們構建它的相同架構上運行。
我們可以使用 GOOS 和 GOARCH 環境變量爲不同的架構創建不同的二進制文件,如下所示:
GOOS=windows GOARCH=amd64 go build hello.go
這將會創建 hello.exe 文件,可以在 64 位的 Windows 機器上運行:
Hello.exe 執行
設置 64 位的 Mac 的環境變量爲 GOOS=darwin GOARCH=amd64 ,Linux 的環境變量是GOOS=linux GOARCH=amd64。
這是 Go 最好的特性之一。
Go 的工作空間
關於 Go 中的一個特殊的點,我們稱爲工作區。
這個工作區是 Go 中的 “家目錄”。
Go 默認的路徑在 $HOME/go 下,所以你可以在你的家目錄中看到 go 文件夾。
它會在你安裝包(待會兒我們看一下)進行創建。
例如我在 VS Code 中加載 hello.go 文件那一刻,它提示我安裝[ gopls ](https://pkg.go.dev/golang.org/x/tools/gopls) 命令,開發調試工具(dlv), 和靜態檢查行。
它們在 $HOME/go 下自動安裝:
$HOME/go
當你使用 go install 安裝庫時,它們會保存在這裏。
這就是我們所說的 GOPATH。
你可以修改環境變量 GOPATH 以更改 Go 安裝包的位置。
這在同時處理不同的項目並且希望隔離所使用的庫時非常有用。
深入 Go 語言
現在我們已經理解了第一部分,我們運行了第一個 Hello, World! 程序, 我們可以深入 Go 語言了。
該語言沒有語義上有意義的空格。它像 C、C++、Rust、Java、JavaScript,但是不像 Python,其中空格是有意義的,用於創建塊,而不是花括號。
分號是可選的,就像在 JavaScript 中一樣(不同於 C、C++、Rust 或 Java)。
Go 非常重視縮進和視覺順序。
在我們安裝 Go 程序的時候自帶了 gofmt 命令,我們可以用它對 Go 程序進行格式化。VS Code 中在底層使用它對 Go 源碼文件進行格式化。
這是非常有趣和創新的,因爲格式和問題,如製表符與空格或 “我應該把花括號放在循環定義的同一行還是下一行”,都是浪費時間。
語言創造者定義了規則,每個人都使用這些規則。
這對於擁有大型團隊的項目非常有用。
我推薦你在 VS Code 中設置 “保存時格式化” 和 “粘貼時格式化”:
在 VS Code 中設置 Go 的粘貼時格式化和保存時格式化
你可以使用常用的 C / C++ / JavaScript / Java 語法在 Go 中寫註釋:
// this is a line comment
/*
multi
line
comment
*/
Go 中的變量
在編程語言中,首先要做的事情之一是定義變量。
在 Go 中我們用 var 關鍵字聲明變量:
var age = 20
你可以在包級別定義變量:
package main
import "fmt"
var age = 20
func main() {
fmt.Println("Hello, World!")
}
或是在函數中:
package main
import "fmt"
func main() {
var age = 20
fmt.Println("Hello, World!")
}
在包級別定義,變量在組成包的所有文件中都可見。一個包可以由多個文件組成,你只需要創建另一個文件並在頂部使用相同的包名。
在函數級別定義,變量僅在該函數中可見。它在調用函數時初始化,在函數結束執行時銷燬。
我們使用以下示例:
var age = 20
我們設置變量 age 的值爲 20。
這使得 Go 確定變量 age 的類型是 int。
稍後我們將看到更多關於類型的信息,但你應該知道有很多不同的類型,從 int、string 和 bool 開始。
我們可以不給變量設置值,但是必須設置它們的類型如下:
var age int
var name string
var done bool
當你知道值時,你可以直接用短的變量聲明如 := 運算符:
age := 10
name := "Roger"
變量名你可以使用小寫字母,數字和 _ 或者可以使用類似於 _ 的其他字符。
名字是區分大小寫的。
如果名字太長,通常可使用駝峯命名法,例如我們想表現車的名字就用 carName。
你可以使用賦值運算符 = 給一個變量賦予新的值。
var age int
age = 10
age = 11
如果你有一個變量在編程過程中永遠都不會變,你可以使用 const 關鍵字來聲明這個變量:
const age = 10
你可以一行代碼中聲明多個變量:
var age, name
將它們全部都初始化:
var age, name = 10, "Roger"
//or
age, name := 10, "Roger"
在程序中使用未聲明的變量,程序會報錯,而且無法通過編譯。
在 VS Code 中你可以看到警告如下:
使用未聲明變量的警告
以下是編譯的報錯:
使用未聲明變量的編譯器報錯
如果你聲明瞭一個變量且沒有給這個變量一個初始值,它會自動初始化一個對應類型的初始值,例如 integer 類型的值爲 0 而字符串的值是一個空的字符串。
Go 的基礎類型
Go 是一門強類型語言。
我們可以看到你怎麼定義一個變量,指定它的類型:
var age int
或者你可以直接給變量賦予初始值,讓 Go 來推斷它的類型:
var age = 10
這些是 Go 中的基礎類型:
-
整型(
int,int8,int16,int32,rune,int64,uint,uintptr,uint8,uint16,uint64) -
浮點型(
float32,float64),用於表示帶小數點的數 -
複數類型(
complex64,complex128),常用於科學計算中 -
字符型(
byte),表示一個 ASCII 字符 -
字符串(
string), 一個byte的集合 -
布爾型(
bool)表示 true 或 false
我們有很多不同類型的整數類型,在大多數情況下你只會用到 int,並且你可能會選擇一個更專業的方法進行優化(而不是在學習時需要考慮的事情)。
在你使用 64 位系統的時候 int 類型默認爲 64 位, 使用 32 位系統的時候 int 類型默認爲 32 位,其他的與此類似。
uint 類型是無符號的 int 類型,如果你知道這個數字不是負數,你可以用這個類型存儲比現在大兩倍的數字。
所有的基礎類型都是值類型, 這意味着當它們作爲參數傳遞或從函數返回時,它們通過值傳遞給函數。
Go 中的字符串
Go 中的字符串是 byte 序列。
像我們所看到的一樣,你可以定義字符串如下:
var name = "test"
其中很重要一點是不像其他語言,字符串定義只能使用雙引號表示,而不是單引號。
獲得字符串的長度,可以使用內置函數 len():
len(name) //4
你可以用方括號訪問單獨字符,使用索引來取得你想要的字符:
name[0] //"t" (indexes start at 0)
name[1] //"e"
你可以使用切片獲取到字符串:
name[0:2] //"te"
name[:2] //"te"
name[2:] //"st"
你可以創建一個字符串的副本:
var newstring = name[:]
你可以將字符串賦值給一個新的變量,如下:
var first = "test"
var second = first
字符串是 不可變 的, 所以你無法修改字符串的值。
如果你給 first 賦予一個新值,second 的值依然是 "test":
var first = "test"
var second = first
first = "another test"
first //"another test"
second //"test"
字符串是引用類型,意味着如果你將一個字符串傳遞給一個方法,字符串引用會被複制,而不是它的值,但是字符串是不可變的,所在在使用過程中它和 int 類型並沒有很大的區別,例如。
你可以通過 + 運算符連接兩個字符串:
var first = "first"
var second = "second"
var word = first + " " + second //"first second"
Go 提供了 strings 庫來進行字符串的操作。
我們已經知道怎麼在 “Hello, World!” 的案例中導入一個包。
這裏你可以導入 strings:
package main
import (
"strings"
)
你可以使用它了。
在例子中我們使用 HasPrefix() 函數來判斷一個字符串的開頭是否是以另一個子串開頭的:
package main
import (
"strings"
)
func main() {
strings.HasPrefix("test", "te") // true
}
你可以在這找到所有的函數列表:https://pkg.go.dev/strings。
以下是你經常會用到的函數列表:
-
strings.ToUpper()返回一個新的字符串, 大寫 -
strings.ToLower()返回一個新的字符串, 小寫 -
strings.HasSuffix()檢查是否以某子串結尾 -
strings.HasPrefix()檢查是否以某子串開頭 -
strings.Contains()檢查是否包含某字串 -
strings.Count()計算一個某子串在當前字符串出現的次數 -
strings.Join()創建一個新的字符串並連接多個字符串 -
strings.Split()創建一個數組來保存通過特殊字符串對字符串進行分割的結果,例如通常使用空格 -
strings.ReplaceAll()使用替換,可以使用一個新的字符串替換掉原字符中的字符串
Go 中的數組
數組是單個類型的序列。
我們可以這種方式定義數組:
var myArray [3]string //an array of 3 strings
或者你也可以給數組賦予初始值:
var myArray = [3]string{"First", "Second", "Third"}
在這個例子中,你可以讓 Go 來幫你進行數組長度的推斷:
var myArray = [...]string{"First", "Second", "Third"}
數組只能包含同一種類型的數據。
數組不能動態擴容——在 Go 中你必須聲明數組的長度。這和類型一樣是數組的一部分。當然,你不能使用一個沒有聲明長度的數組。
由於這個限制,數組在 Go 中很少使用,我們經常用到切片(稍後我們會講到更多)。切片的底層是數組。所以我們需要知道它的工作原理。
你可以通過中括號獲得數組中的每一個值正如我們之前獲取字符串中單個字符一樣:
myArray[0] //indexes start at 0
myArray[1]
你可以給數組中的成員設置新的值:
myArray[2] = "Another"
你可以通過 len() 函數來獲取數組的長度:
len(myArray)
數組是 值類型。這意味着複製一個數組:
anotherArray := myArray
傳遞一個數組給一個函數,或者一個函數返回數組,創建的都是原數組的副本。
這也是和其他編程語言的不同之處。
這裏創建一個簡單的示例,我們將一個新的值賦值給副本的一個元素,這個過程中不會修改原數組的元素:
var myArray = [3]string{"First", "Second", "Third"}
myArrayCopy := myArray
myArray[2] = "Another"
myArray[2] //"Another"
myArrayCopy[2] //"Third"
記住你只能在數組中加入同一類型的數據,所以在例子中設置 myArray[2] = 2 會報錯。
底層的元素存儲在連續的內存當中。
Go 中的切片
切片是一種很像數組的數據結構,但它的大小是可以改變的。
底層,切片使用了數組且它是建立在數組之上的抽象概念使得本身變得更加靈活和方便使用(可以把數組想象爲底層結構)。
你可以把切片作爲高級語言中使用數組的一種使用方式。
你可以類似於數組定義切片,省略長度:
var mySlice []string //a slice of strings
你可以初始化切片的值:
var mySlice = []string{"First", "Second", "Third"}
//or
mySlice := []string{"First", "Second", "Third"}
你可以用 make() 函數創建一個有明確長度的空切片:
mySlice := make([]string, 3) //a slice of 3 empty strings
你可以用已經存在的切片創建一個新的切片,添加一個或多個元素進去:
mySlice := []string{"First", "Second", "Third"}
newSlice := append(mySlice, "Fourth", "Fifth")
注意我們需要給 append() 的結果賦給一個新的切片,否則我們將得到一個編譯器錯誤。原來的切片沒有改變,我們將得到一個全新的切片。
你也可以使用 copy() 函數來創建一個重複的切片,這樣它就不會共享另一個切片的內存,而是獨立的:
mySlice := []string{"First", "Second", "Third"}
newSlice := make([]string, 3)
copy(newSlice, mySlice)
如果你複製的切片沒有足夠的空間(比原來的短),則只複製第一個元素(只到有空間爲止)。
你可以從數組中初始化一個切片:
myArray := [3]string{"First", "Second", "Third"}
mySlice = myArray[:]
多個切片可以使用與底層數組相同的數組:
myArray := [3]string{"First", "Second", "Third"}
mySlice := myArray[:]
mySlice2 := myArray[:]
mySlice[0] = "test"
fmt.Println(mySlice2[0]) //"test"
這兩個切片現在共享同一塊內存。修改一個切片底層數組也會跟着改變,並導致從該數組生成的另一個切片也會被修改。
與數組一樣,每一個切片中的元素同樣存儲在內存的連續內存之中。
如果你知道你必須用到切片,你可以在初始化的時候設置所需的容量。這種方式,在你需要更多空間的時候,這些空間已經準備好了(替代了選擇和移動切片到從老的內存空間到新的內存的方式,並減少了垃圾回收)。
我們可以通過 make() 函數的第三個參數來指定容量:
newSlice := make([]string, 0, 10)
//an empty slice with capacity 10
例如字符串,你可以以下語法獲得切片中的一部分:
mySlice := []string{"First", "Second", "Third"}
newSlice := mySlice[:2] //get the first 2 items
newSlice2 := mySlice[2:] //ignore the first 2 items
newSlice3 := mySlice[1:3] //new slice with items in position 1-2
Go 中的 map
map 是 Go 中一種常見的數據類型。
在其他語言被稱爲字典或 哈希表 或關聯數組。
下面是創建一個 map 的方式:
agesMap := make(map[string]int)
你不需要對 map 設置容納多少項。
你可以通過這種方式添加新的元素到 map 中:
agesMap["flavio"] = 39
你可以用以下語法初始化 map 並賦值:
agesMap := map[string]int{"flavio": 39}
你可以通過鍵來獲取對應的值:
age := agesMap["flavio"]
你可以通過 delete() 函數來刪除 map 中的元素:
delete(agesMap, "flavio")
Go 中的循環
Go 中最好的特性是它只提供最少的選擇。
我們只有一個循環關鍵字:for。
你可以像這樣使用它:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
我們首先初始化一個循環的變量, 我們設置一個條件用於檢查我們的循環是否應該結束。最後我們設置 post 語句,在每一次循環後執行, 這裏例子中是增長 i。
i++ 增長 i 變量.
< 運算符 用於比較 i 和 10 的值, 會返回 true 或 false, 決定循環體是否執行。
我們不需要用圓括號來包圍代碼塊,與 C 和 JavaScript 不太一樣。
其他語言有各種不同的循環結構,但是 Go 中只有這一個,我們可以有像 while 一樣的循環,如果你熟悉一門有它的語言,像這樣:
i := 0
for i < 10 {
fmt.Println(i)
i++
}
我們完全可以忽略條件,在我們想要中止可以使用 break:
i := 0
for {
fmt.Println(i)
if i < 10 {
break
}
i++
}
我在循環體中使用了一個 if 聲明,但是在我們沒有看到條件語句!我們下一步看。
我現在想要介紹一種東西 range。
我們可以使用以下語法通過 for 來迭代數組:
numbers := []int{1, 2, 3}
for i, num := range numbers {
fmt.Printf("%d: %d\n", i, num)
}
//0: 1
//1: 2
//2: 3
注意:我使用 fmt.Printf() 它允許我們使用佔位符 %d,意思是整數,而 \n 意思是加入一個換行符。
在你不需要索引時,一般可以使用以下語法:
for _, num := range numbers {
//...
}
我們可以使用 _ 語法,它表示 “忽略 這個”,以避免 Go 編譯器產生一個錯誤:“你沒有使用 i 變量!”。
Go 中的條件語句
我們使用 if 聲明一個條件從而執行不同的代碼:
if age < 18 {
//underage
}
else 部分是可選的:
if age < 18 {
//underage
} else {
//adult
}
或者可以使用多個 if:
if age < 12 {
//child
} else if age < 18 {
//teen
} else {
//adult
}
如果你在 if 中定義了任何的變量,那麼只能在 if 中使用(else 中也一樣且你需要在 {} 中寫新的代碼塊)。
如果你有很多不同的 if 聲明來檢查同一個條件,使用 switch 是更好的選擇:
switch age {
case 0: fmt.Println("Zero years old")
case 1: fmt.Println("One year old")
case 2: fmt.Println("Two years old")
case 3: fmt.Println("Three years old")
case 4: fmt.Println("Four years old")
default: fmt.Println(i + " years old")
}
與 C, JavaScript 和其他語言相比,你不需要在每一個 case 中寫 break。
Go 中的運算符
到目前爲止,我們已經在代碼示例中使用了一些運算符, 如 =、:= 和 <。
讓我們瞭解更多。
我們有賦值運算符 = 和 := 我們用來定義和初始化變量:
var a = 1
b := 1
我們有比較運算符 == 和 != 用來比較兩個參數並會返回一個布爾值:
var num = 1
num == 1 //true
num != 1 //false
還有 <, <=, >, >=:
var num = 1
num > 1 //false
num >= 1 //true
num < 1 //false
num <= 1 //true
我們有雙元(要求有兩個參數)算術運算符, 像 +、-、*、/、%.
1 + 1 //2
1 - 1 //0
1 * 2 //2
2 / 2 //1
2 % 2 //0
+ 可以用來連接字符串:
"a" + "b" //"ab"
我們有單元運算符 ++ 和 -- 用來對一個數字進行自增或自減:
var num = 1
num++ // num == 2
num-- // num == 1
注意:不像 C 和 JavaScript 像這樣 ++num 讓一個數字預先操作。當然,運算符不會返回任何值。
我們有邏輯運算符來幫我們進行基本的 true 和 false 的判斷,使用:&&、|| 和 !:
true && true //true
true && false //false
true || false //true
false || false //false
!true //false
!false //true
這三個是最主要。
Go 中的結構體
結構體 是一種 類型,它包括一個或多個變量。它像是一個變量的集合。我們將它們稱爲字段。它們可以有不同的類型。
這是一個結構體定義的示例:
type Person struct {
Name string
Age int
}
注意這裏我們使用大寫的名字作爲字段名,不然它們在包內將會是私有的。且當你想讓結構體作爲參數傳遞給另一個庫的函數時,像我們使用 JSON 及數據庫時,這些字段都將無法訪問。
一旦我們定義了一個結構體,我們就可以用這個類型初始化一個變量:
flavio := Person{"Flavio", 39}
且我們可以用以下語法修改字段的值:
flavio.Age //39
flavio.Name //"Flavio"
你也可以使用這種方式將一個結構體初始化賦給一個變量:
flavio := Person{Age: 39, Name: "Flavio"}
你也可以只初始化一個字段:
flavio := Person{Age: 39}
或者不初始化任何值:
flavio := Person{}
//or
var flavio Person
或者在之後設值:
flavio.Name = "Flavio"
flavio.Age = 39
結構體很常用,因爲你可以對不相關的數據進行分組,並將其傳遞給函數或從函數傳遞給函數,保存在切片中,等等。
一旦定義,結構體就是像 int 或 string 這樣的類型,這意味着你也可以在其他結構中使用它:
type FullName struct {
FirstName string
LastName string
}
type Person struct {
Name FullName
Age int
}
Go 中的函數
一個函數是一塊代碼,它被賦予了一個名字且包含了一些指令。
在 “Hello, World!” 示例中我們創建了一個 main 函數, 那是程序的入口。
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
這是一個特殊的函數。
通常我們使用自定義的名字來定義函數:
func doSomething() {
}
你可以像這樣調用它:
doSomething()
一個函數可以設置入參,我們需要給參數設置類型,如下:
func doSomething(a int, b int) {
}
doSomething(1, 2)
a 和 b 是函數內部參數的名字。
一個函數可以返回一個值,像這樣:
func sumTwoNumbers(a int, b int) int {
return a + b
}
result := sumTwoNumbers(1, 2)
注意這裏我們指定的返回的值的類型。
一個函數可以返回多個或一個值:
func performOperations(a int, b int) (int, int) {
return a + b, a - b
}
sum, diff := performOperations(1, 2)
有趣的是很多語言只能返回一個值。
函數內部定義的任何變量都是函數的本地變量。
一個函數也可以不限制參數的個數,且這樣的函數我們稱它爲可變函數:
func sumNumbers(numbers ...int) int {
sum := 0
for _, number := range numbers {
sum += number
}
return sum
}
total := sumNumbers(1, 2, 3, 4)
Go 中的指針
Go 支持使用指針。
假設你有一個變量:
age := 20
使用 &age 你獲得這個變量的指針,它是內存地址。
當你擁有一個變量的指針時,你可以使用 * 運算符獲取它的值:
age := 20
ageptr = &age
agevalue = *ageptr
通常當你想要傳遞一個參數給你調用的函數時。Go 默認會在函數內部複製這個變量的值,所以這不會改變 age 的值:
func increment(a int) {
a = a + 1
}
func main() {
age := 20
increment(age)
//age is still 20
}
你可以像這樣使用指針:
func increment(a *int) {
*a = *a + 1
}
func main() {
age := 20
increment(&age)
//age is now 21
}
Go 中的方法
你可以給一個結構賦值一個函數,在這種情況下我們稱它爲 “方法”。
示例:
type Person struct {
Name string
Age int
}
func (p Person) Speak() {
fmt.Println("Hello from " + p.Name)
}
func main() {
flavio := Person{Age: 39, Name: "Flavio"}
flavio.Speak()
}
你可以定義一個方法爲指針接收或值接收。
上述例子中我們展示的是值接收,這個接收器會複製結構體的結構。
這裏將會有一個指針接收,接收結構實例的指針:
func (p *Person) Speak() {
fmt.Println("Hello from " + p.Name)
}
Go 中的接口
接口是一個類型,可以定義一個或多個方法聲明。
方法沒有被實現,只有它們的簽名:名字、參數類型和返回值類型。
像這樣:
type Speaker interface {
Speak()
}
現在你有一個函數,可以接納任何類型來實現接口定義所有方法:
func SaySomething(s Speaker) {
s.Speak()
}
我們可以讓任何一個結構體來實現這些方法:
type Speaker interface {
Speak()
}
type Person struct {
Name string
Age int
}
func (p Person) Speak() {
fmt.Println("Hello from " + p.Name)
}
func SaySomething(s Speaker) {
s.Speak()
}
func main() {
flavio := Person{Age: 39, Name: "Flavio"}
SaySomething(flavio)
}
更多內容
這份手冊初步介紹了 Go 編程語言。
基於這裏的基礎,你現在可以學更多東西:垃圾回收、錯誤處理、併發和網絡、文件系統接口,等等。
海闊憑魚躍,天高任鳥飛。
我的建議是選擇一個你想要創建的程序,然後開始學習你需要的東西。
它是有趣且值得做的。
原文鏈接:https://www.freecodecamp.org/news/go-beginners-handbook/
作者:Flavio Copes
譯者:herosql
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/64fpmzL7jmPdbXi080grWA