在 Go 中,struct 是一連串具名的元素,這些元素稱為_欄位_,每個欄位都有名稱和型別。
欄位名稱在 struct 內必須是唯一的。
struct 可以比擬為物件導向程式設計範式中的_類別_。
使用 type 和 struct 關鍵字可以建立新的 struct,並明確地定義每個欄位名稱和型別。
例如,這裡我們定義一個 Shape struct,它包含形狀的名稱和大小:
type Shape struct {
name string
size int
}
struct 中的欄位名稱遵循 Go 的慣例:以小寫字母開頭的欄位,只有同一個套件中的程式碼能存取;以大寫字母開頭的欄位,則可以在其他套件中存取。
定義好 struct 之後,你需要建立新的實例,並以任意順序使用欄位名稱來定義各個欄位:
s := Shape {
name: "Square",
size: 25,
}
要讀取或修改實例的欄位,請使用 . 表示法:
// Update the name and the size
s.name = "Circle"
s.size = 35
fmt.Printf("name: %s size: %d\n", s.name, s.size)
// Output: name: Circle size: 35
沒有指定初始值的欄位,會擁有它們的零值。 例如:
s := Shape{name: "Circle"}
fmt.Printf("name: %s size: %d\n", s.name, s.size)
// Output: name: Circle size: 0
只要依_順序_定義欄位,就可以不使用欄位名稱來建立 struct 的實例:
s := Shape {
"Oval",
20,
}
不過,這種語法被視為_脆弱的程式碼_,因為當新增欄位時,特別是新增的欄位型別不同時,它就可能會壞掉。
在下列範例中,我們為 Shape 新增了一個額外的欄位:
type Shape struct {
name string
description string // new field 'description' added
size int
}
s := Shape{
"Circle",
20,
}
// Since the second field of the struct is now a string and not an int,
// the compiler will throw an error when compiling the program:
// Output: cannot use 20 (type untyped int) as type string in field value
// Output: too few values in Shape{...}
有時候,有幾個能幫助我們建立 struct 實例的函式會很方便。
依照慣例,這些函式通常叫做 New,或名稱以 New 開頭,但因為它們只是一般的函式,你可以任意命名。
它們或許會讓你想起其他語言中的建構子,但在 Go 中,它們就只是一般的函式。
在下列範例中,我們使用其中一個 New 函式來建立 Shape 的新實例,並自動為形狀的 size 設定預設值:
func NewShape(name string) Shape {
return Shape{
name: name,
size: 100, //default-value for size is 100
}
}
NewShape("Triangle")
// => Shape { name: "Triangle", size: 100 }
使用 New 函式有下列優點:
New 函式通常和它們所初始化的 struct 宣告在同一個套件中,因此它們甚至可以初始化 struct 的私有欄位在這個練習中,你會負責籌辦各種遙控車之間的比賽。每輛車都有自己的速度和耗電特性。
車子一開始的電池都是滿的(100%)。 每次你用遙控器駕駛車子時,車子會移動相當於自身速度的距離(單位為公尺),剩餘的電池百分比則會依耗電量下降。
如果車子的電池低於耗電百分比,就無法再駕駛這輛車。
每條賽道都有自己的距離。測試車子的方式,是檢查它能不能在電池耗盡前跑完整條賽道。
定義一個Car結構體,其中包含下列int型別的欄位:
定義一個函式NewCar,讓你能建立遙控車。這個函式接受兩個參數:車子的速度(單位為公尺)和耗電百分比,兩者的型別都是int,並回傳一個Car實例:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
// => Car{speed: 5, batteryDrain: 2, battery:100, distance: 0}
定義另一個名為Track的結構體型別,其中的distance欄位型別為整數。
定義一個函式NewTrack,讓你能建立賽道。這個函式只有一個參數,就是賽道的距離(單位為公尺),型別為int:
distance := 800
track := NewTrack(distance)
// => Track{distance: 800}
實作Drive函式,它會根據車子的速度更新已行駛的公尺數,並依耗電量減少電池電量。
如果電池不足以再行駛一次,車子就不會移動:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
car = Drive(car)
// => Car{speed: 5, batteryDrain: 2, battery: 98, distance: 5}
要完成一場比賽,車子必須能行駛完整條賽道的距離,也就是在越過終點線之前,電池不能先耗盡。
實作CanFinish函式,它會接受一個Car實例和一個Track實例作為參數,並在車子能完成比賽時回傳true,否則回傳false。
假設你現在位於比賽的起跑線,正要發動車子的引擎來參加比賽。 請注意,開始比賽時,車子的電池不一定處於滿電狀態:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
distance := 100
track := NewTrack(distance)
CanFinish(car, track)
// => true