「每週譯 Go」在 Go 中定義結構體


簡介

圍繞具體的細節建立抽象,是編程語言能給開發者的最大工具。

結構體使我們可以談論 Address 而不是通過描述 Street, City, 或 PostalCode 字符串來進行推斷。

它們作爲文檔_(點擊跳轉查看哦)_的一個自然紐帶,致力於告訴未來的開發者(包括我們自己)哪些數據對我們的 Go 程序是重要的,以及未來的代碼應該如何正確使用這些數據。

結構體可以用幾種不同的方式來定義和使用。

在本教程中,我們將會逐一看下這些技術。

定義結構體

結構體的工作方式類似於你可能正在使用的紙質表格,例如用來報稅的表單。

紙質表格可能有文本信息的字段,比如你的名字和姓氏。除了文本字段外,表單可能還有複選框來表示布爾值,如 “已婚” 或“單身”,或表示出生日期的日期字段。

同樣,結構體將不同數據收集在一起,並通過不同的字段名組織它們。當你用一個新的結構體初始化一個變量時,就好像你影印了一張表格並準備填寫。

要創建一個新的結構體,你必須首先給 Go 定義一個藍圖來描述結構體所包含的字段。

這個結構定義通常以關鍵字 type 開始,緊跟着結構體的名稱。隨後,使用 struct 關鍵字,後面跟着一對大括號 {},在這裏聲明結構體將包含的字段。

一旦你定義了結構體,就可以聲明使用該結構體定義的變量。

本例定義了一個結構並使用它。

package main

import "fmt"

type Creature struct {
 Name string
}

func main() {
 c := Creature{
  Name: "Sammy the Shark",
 }
 fmt.Println(c.Name)
}

當你運行這段代碼時,會看到這樣的輸出:

output
Sammy the Shark

在這個例子中,我們首先定義了一個 Creature 結構體,包含一個字符串類型的 Name 字段。

main 方法中,我們通過在 Creature 類型名稱後添加一對大括號來創建一個 Creature 實例,然後爲該實例的字段設定值。c 實例的 Name 字段將被設置爲 “Sammy the Shark”。

fmt.Println 方法的調用中,我們通過在實例變量後加點號與我們想訪問的字段名來檢索實例的字段值。

例如,c.Name 在本例中返回 Name 字段值。當你聲明一個新的結構體實例時,通常會列舉字段名和它們的值,就像上一個例子。

此外,如果每個字段的值都會在結構的實例化過程中提供,也可以省略字段名,如本例:

package main

import "fmt"

type Creature struct {
 Name string
 Type string
}

func main() {
 c := Creature{"Sammy""Shark"}
 fmt.Println(c.Name, "the", c.Type)
}

輸出結果與上一個例子相同:

output
Sammy the Shark

我們爲 Creature 增加了一個額外的字段,用字符串類型來追蹤生物 Type

當在 main 方法中實例化 Creature 時,我們選擇使用較短的實例化方式,即按順序爲每個字段提供值,並省略其字段名。

Creature{"Sammy", "Shark"} 的聲明中,因爲 Name 在類型聲明中首先出現,然後爲Type,所以Name 字段取值爲 SammyType 字段取值爲 Shark

這種較短的聲明方式有一些缺點,導致 Go 社區在大多數情況下都傾向於採用較長的方式。

使用簡短聲明時,你必須爲結構體中的每個字段提供值,而不能省略你並不關心的字段。

這很快就會導致包含很多字段的結構體短聲明變得混亂。出於這個原因,簡短聲明通常用於字段少的結構體。

到目前爲止,例子中的字段名都是以大寫字母開頭。這並不僅是風格上的偏好,而是在字段名中使用大寫或小寫字母會影響到你的字段名是否能被其他包中運行的代碼所訪問。

結構體字段導出

結構體的字段遵循與 Go 編程語言中其他標識符相同的導出規則。

如果字段名以大寫字母開頭,則該字段可被定義該結構體的包之外的代碼讀寫。如果字段以小寫字母開頭,則只有該結構體包內的代碼纔可以讀寫該字段。

這個例子定義了可導出和不可導出的字段:

package main

import "fmt"

type Creature struct {
 Name string
 Type string

 password string
}

func main() {
 c := Creature{
  Name: "Sammy",
  Type: "Shark",

  password: "secret",
 }
 fmt.Println(c.Name, "the", c.Type)
 fmt.Println("Password is", c.password)
}

這將輸出:

output
Sammy the Shark
Password is secret

我們在之前的例子中添加了一個額外的字段,secret

secret 是一個未導出的字符串類型字段,這意味着任何試圖實例化 Creature 的其他包將無法訪問或設置其 secret 字段。

在同一個包內,我們能夠訪問這些字段,正如本例所做的那樣。

由於 main 方法也在 main 包中,它能夠引用 c.password 並檢索所存儲的值。在結構中擁有未導出的字段是很常見的,對它們的訪問由導出的方法來進行配置。

內聯結構體

除了定義一個新的類型來表示一個結構體外,你還可以定義一個內聯結構。

在爲結構類型想一個新的名稱會造成浪費的情況下,這些即時創建的結構定義 (不需要命名的 struct) 會非常有用。

例如,測試經常使用一個結構體來定義構成一個特定測試案例的所有參數。當該結構只會在一個地方使用時,想出 CreatureNamePrintingTestCase 這樣的新名字會很麻煩。

內聯結構定義出現在變量賦值的右側。你必須立即使用一對額外的大括號進行實例化併爲定義的每個字段賦值。

下面的例子顯示了一個內聯結構定義:

package main

import "fmt"

func main() {
 c := struct {
  Name string
  Type string
 }{
  Name: "Sammy",
  Type: "Shark",
 }
 fmt.Println(c.Name, "the", c.Type)
}

這個例子的輸出結果將是:

output
Sammy the Shark

本例沒有使用 type 關鍵字定義一個新的類型來描述我們的結構體,而是通過將 struct 定義放在短賦值運算符 := 之後定義一個內聯結構。

我們像之前的例子一樣定義了結構體的字段,但是必須立即提供一對大括號和每個字段將賦的值。

現在我們可以和以前完全一樣使用這個結構體,用點符號來訪問字段名。

內聯結構最常用於測試過程中聲明,因爲經常會需要用到一次性結構體來定義包含特定測試案例的數據和預期測試結果。

總結

結構體是開發者爲組織信息而定義的各種各樣的數據的集合。

大多數程序都要處理大量的數據,如果沒有結構體,就很難記住哪些 stringint 變量相關,哪些無關。

下一次,當你發現自己在組織一些變量時,問問自己,也許這些變量用 struct 來分組會更好。這些變量可能一直都在描述更高層級的概念。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/J7w8IN5Gw94AzOZlnYxvow