Go語言結構體字面量

2020-07-16 10:05:09
結構體型別的值可以通過結構體字面量來設定,即通過設定結構體的成員變數來設定。

type Point struct{ X, Y int }
p := Point{1, 2}

有兩種格式的結構體字面量。第一種格式如上所示,它要求按照正確的順序,為每個成員變數指定一個值。這會給開發和閱讀程式碼的人增加負擔,因為他們必須記住每個成員變數的順序,另外這也使得未來結構體成員變數擴充或者重新排列的時候程式碼維護性差。

所以,這種格式一般用在定義結構體型別的包中或者一些有明顯的成員變數順序約定的小結構體中,比如image.Point{x, y}或者color.RGBA{red, green, blue, alpha}

我們用得更多的是第二種格式,通過指定部分或者全部成員變數的名稱和值來初始化結構體變數:

anim := gif.GIF{LoopCount: nframes}

如果在這種初始化方式中某個成員變數沒有指定,那麼它的值就是該成員變數型別的零值。因為指定了成員變數的名字,所以它們的順序是無所謂的。

這兩種初始化方式不可以混合使用,另外也無法使用第一種初始化方式來繞過不可導岀變數無法在其他包中使用的規則。

package p
type T struct{ a,b int }   // a 和 b 都是不可匯出的

package q
import "p"
var _ = p.T{a: 1, b: 2}      //編譯錯誤,無法參照 a、b
var _ = p.T{l, 2}               //編譯錯誤,無法參照 a、b

雖然上面的最後一行程式碼沒有顯式地提到不可匯出變數,但是它們被隱式地參照了,所以這也是不允許的。

結構體型別的值可以作為引數傳遞給函數或者作為函數的返回值。例如,下面的函數將 Point 縮放了一個比率:

func Scale(p Point, factor int) Point {
    return Point{p.X * factor, p.Y * factor}
}

fmt.Println(Scale(Point{1, 2}» 5)) // "{5 10}"

出於效率的考慮,大型的結構體通常都使用結構體指標的方式直接傳遞給函數或者從函數中返回。

func Bonus(e *Employee, percent int) int {
    return e.Salary * percent / 100
}

這種方式在函數需要修改結構體內容的時候也是必需的,在Go語言這種按值呼叫的程式語言中,呼叫的函數接收到的是實參的一個副本,並不是實參的參照。

func AwardAnnualRaise(e *Employee) {
    e.Salary = e.Salary * 105 / 100
}

由於通常結構體都通過指標的方式使用,因此可以使用一種簡單的方式來建立、初始化一個 struct 型別的變數並獲取它的地址:

pp := &Point{1, 2}

這個等價於:

pp := new(Point)
*pp = Point{1, 2}

但是 &Point{1, 2} 這種方式可以直接使用在一個表示式中,例如函數呼叫。

結構體比較

如果結構體的所有成員變數都可以比較,那麼這個結構體就是可比較的。兩個結構體的比較可以使用 == 或者 !=。其中 == 操作符按照順序比較兩個結構體變數的成員變數,所以下面的兩個輸出語句是等價的:

type Point struct{ X, Y int }

p := Point{1, 2}
q := Point{2, 1}
fmt.Println(p.X == q.X && p.Y == q.Y) // "false"
fmt.Println(p == q)                                // "false"

和其他可比較的型別一樣,可比較的結構體型別都可以作為 map 的鍵型別。

type address struct {
    hostname string
    port int
}
hits := make(map[address]int)
hits[address{"golang.org", 443}]++