结构体是一种集合类型,可以包含任意个字段。

集合的字段也是变量,可以为字段赋值、对字段取地址。

读取结构体元素用 . 符号;. 也可用在指向结构体的指针上。

type Employee struct {
    ID int
    Name string
    Address string
    DoB time.Time
    Position string
    Salary int
    ManagerID int
}
var dilbert Employee
position := &dilbert.Position // 等价于 &(dilbert.Position)
*position = "Senior " + *position

var employeeOfTheMonth *Employee = &dilbert
employeeOfTheMonth.Position += " (proactive team player)"
// 等价于
(*employeeOfTheMonth).Position +=  " (proactive team player)"

// 结构体和结构体字面量 https://play.golang.org/p/ocySDzIc2Pr

字段顺序不同(每行的顺序,同类型字段是否结合写、结合写的顺序),结构体的类型就不同。

结构体类型 S 里不能定义类型为 S 的字段:集合类型的值(数组类型也是)不能包含自身。
S 可以里可以定义类型是 *S 的字段,基于这一点可以实现递归的数据结构(如链表、树)。

结构体的零值有它的每个字段的零值构成。

没有字段的结构体称为空结构体(struct{}),它占的空间大小是零。

出于效率上的考虑,大结构体类型通常以指针的形式间接地传入函数或从函数返回。

初始化结构体变量可以用简写,简写可以直接用在表达式中:

// shorthand notation
pp := &Point{1, 2}
// equivalent to
pp := new(Point)
*pp = Point{1, 2}

若结构体里的所有字段都可比较,则结构体本身也可比较。

结构体的嵌套机制:将一个具名结构体类型作为另一个结构体类型的匿名字段,提供访问内层字段的语法糖(x.f 代替 x.d.e.f)。

type Point struct {
    X, Y int
}
type Circle struct {
    Point
    Radius int
}
type Wheel struct {
    Circle
    Spokes int
}
var w Wheel
w.X = 8         // equivalent to w.Circle.Point.X = 8
w.Radius = 5    // equivalent to w.Circle.Radius = 5
w.Spokes = 20

匿名字段其实有(隐式)名称,名字就是它的具名类型(因此不能有多个同一类型的匿名字段),只是在 . 表达式中可以省略而已,可以省略任意或是全部的匿名字段。

由于匿名字段的名称由它的类型决定,所以匿名字段的可见性也由它的类型决定。

// point 和 circle 是不导出的
w.X = 8                 // ok
w.circle.point.X = 8    // forbidden

任何具名类型或指向具名类型的指针都可以用作匿名字段。

使用语法糖的形式时,外层的结构体不光拥有匿名字段,同时获得了匿名字段对应的类型的方法。