一、前言

本文主要以下几方面介绍Go语言中的结构体:

  • 初识结构体
  • 结构体初体验
  • 结构体操作和比较
  • 结构体自定义方法
  • 结构体方法接收者类型
  • 结构体嵌套

二、初识结构体

2.1 基本含义

Go语言中的结构体(Struct)是一种用户自定义的复合数据类型,用于组合不同类型的数据项。

结构体可以用于存储和操作复杂的数据结构,以及创建自定义数据类型。通过自定义结构体创建的变量,可以存储不同类型的数据字段。

2.2 基本语法

结构体的基本语法如下:

type MyStruct struct {
    // 结构体字段
    Field1 Type1
    Field2 Type2
    // ...
}

上面参数说明:

  • MyStruct 是结构体的类型名称。
  • Field1Field2 等是结构体的字段名称,可以有多个字段。
  • Type1Type2 等是每个字段的数据类型。

三、结构体初体验

下面使用两种方式来创建和初始化结构体变量。将下面代码粘贴到00-struct-00.go文件中

package main

import (
    "fmt"
    "reflect"
)

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

func main() {
    // 方式一使用var声明变量
    var p1 people
    p1.Name = "zq"
    p1.Age = 18
    p1.Address = "杭州"
    fmt.Println(p1)
    fmt.Println("p1的类型:", reflect.TypeOf(p1))
    // 方式二使用语法糖定义变量
    p2 := people{
        Name:    "zq",
        Age:     20,
        Address: "北京",
    }
    fmt.Println(p2)
    fmt.Println("p2的类型:", reflect.TypeOf(p2))

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
    "fmt"
    "reflect"
)

在这一部分,您导入了两个标准库的包:fmt 用于格式化输出,reflect 用于反射操作,主要用于获取变量的类型信息。

部分 2 - 定义结构体类型

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

这里定义了一个名为 people 的结构体类型,它有四个字段:Name(字符串类型)、Age(整数类型)、Address(字符串类型)和 Gender(字符串类型)。

部分 3 - 主函数

func main() {
    // ...
}

这是程序的入口函数,所有的代码都将在 main 函数中执行。

部分 4 - 方式一:使用 var 声明变量

var p1 people
p1.Name = "zq"
p1.Age = 18
p1.Address = "杭州"

这一部分演示了如何使用 var 关键字声明 p1 变量,并分别为其字段赋值。p1 是一个 people 类型的结构体变量。

部分 5 - 打印 p1 变量及其类型

fmt.Println(p1)
fmt.Println("p1的类型:", reflect.TypeOf(p1))

这里使用 fmt.Println 打印 p1 变量的值以及使用 reflect.TypeOf 打印 p1 变量的类型。通过反射,可以获得变量的类型信息。

部分 6 - 方式二:使用语法糖定义变量

p2 := people{
    Name:    "zq",
    Age:     20,
    Address: "北京",
}

这一部分演示了如何使用结构体字面值(语法糖)创建并初始化 p2 变量,同样也是一个 people 类型的结构体变量。

部分 7 - 打印 p2 变量及其类型

fmt.Println(p2)
fmt.Println("p2的类型:", reflect.TypeOf(p2))

类似于方式一,这里使用 fmt.Printlnreflect.TypeOf 打印 p2 变量的值和类型。

运行上面代码

$ go run .\00-struct-00.go
{zq 18 杭州 }
p1的类型: main.people
{zq 20 北京 }
p2的类型: main.people

四、结构体操作和比较

4.1 取值

获取结构体字段的值,使用 . 运算符。

下面演示演示如何获取结构体变量的字段值,将下面代码粘贴到01-struct-00.go文件中

package main

import (
    "fmt"
)

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

func main() {
    var p3 people = people{"zq", 88, "合肥", "男"}
    fmt.Println("姓名是:", p3.Name)
    fmt.Println("年龄是:", p3.Age)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
    "fmt"
)

在这一部分中,导入了标准库的 fmt 包,用于格式化输出。

部分 2 - 定义结构体类型

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

这里定义了一个名为 people 的结构体类型,它有四个字段:Name(字符串类型)、Age(整数类型)、Address(字符串类型)和 Gender(字符串类型)。

部分 3 - 主函数

func main() {
    // ...
}

这是程序的入口函数,所有的代码都将在 main 函数中执行。

部分 4 - 创建结构体变量并初始化

var p3 people = people{"zq", 88, "合肥", "男"}

在这一部分,创建了一个 people 类型的结构体变量 p3,并使用结构体字面值为其初始化字段值。

部分 5 - 打印姓名和年龄

fmt.Println("姓名是:", p3.Name)
fmt.Println("年龄是:", p3.Age)

这里使用 fmt.Println 打印了 p3 变量的姓名和年龄字段的值。

运行上面代码

$ go run .\01-struct-00.go
姓名是: zq
年龄是: 88

4.2 改值

修改结构体字段的值,同样使用 . 运算符。

下面演示演示如何修改结构体变量的字段值,将下面代码粘贴到01-struct-01.go文件中

package main

import (
    "fmt"
)

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

func main() {
    var p3 people = people{"zq", 88, "合肥", "男"}
    p3.Age = 20
    p3.Address = "北京"
    fmt.Println("更新后的地址是:", p3.Address)
    fmt.Println("更新后的年龄是:", p3.Age)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
    "fmt"
)

在这一部分中,导入了标准库的 fmt 包,用于格式化输出。

部分 2 - 定义结构体类型

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

这里定义了一个名为 people 的结构体类型,它有四个字段:Name(字符串类型)、Age(整数类型)、Address(字符串类型)和 Gender(字符串类型)。

部分 3 - 主函数

func main() {
    // ...
}

这是程序的入口函数,所有的代码都将在 main 函数中执行。

部分 4 - 创建结构体变量并初始化

var p3 people = people{"zq", 88, "合肥", "男"}

在这一部分,创建了一个 people 类型的结构体变量 p3,并使用结构体字面值为其初始化字段值。最初的年龄是 88,地址是 "合肥"。

部分 5 - 修改字段值

p3.Age = 20
p3.Address = "北京"

在这一部分,修改了 p3 变量的 AgeAddress 字段的值,分别将年龄更新为 20,地址更新为 "北京"。

部分 6 - 打印更新后的字段值

fmt.Println("更新后的地址是:", p3.Address)
fmt.Println("更新后的年龄是:", p3.Age)

运行上面代码

$ go run .\01-struct-01.go

更新后的地址是: 北京
更新后的年龄是: 20

4.3 赋值

将一个结构体变量的值赋给另一个变量,使用 = 运算符。

下面演示如何将结构体变量赋给另一个变量,将下面代码粘贴到01-struct-02.go文件中

package main

import "fmt"

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

func main() {
    var p3 people = people{"zq", 88, "合肥", "男"}
    var p4 people = p3
    fmt.Println("赋值后的p4:", p4)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
    "fmt"
)

在这一部分中,导入了标准库的 fmt 包,用于格式化输出。

部分 2 - 定义结构体类型

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

这里定义了一个名为 people 的结构体类型,它有四个字段:Name(字符串类型)、Age(整数类型)、Address(字符串类型)和 Gender(字符串类型)。

部分 3 - 主函数

func main() {
    // ...
}

这是程序的入口函数,所有的代码都将在 main 函数中执行。

部分 4 - 创建结构体变量并初始化

var p3 people = people{"zq", 88, "合肥", "男"}

在这一部分,创建了一个 people 类型的结构体变量 p3,并使用结构体字面值为其初始化字段值。最初的年龄是 88,地址是 "合肥"。

部分 5 - 赋值操作

var p4 people = p3

在这一部分,创建了另一个 people 类型的结构体变量 p4,并使用 p3 的值来初始化 p4。这是一个有效的赋值操作,将 p3 的值复制给了 p4

部分 6 - 打印结果

fmt.Println("赋值后的p4:", p4)

运行上面代码

$ go run .\01-struct-02.go

赋值后的p4: {zq 88 合肥 }

4.4 比较

使用 == 运算符来比较两个结构体变量是否相等。

下面演示如何判断两个结构体变量是否相等,将下面代码粘贴到01-struct-03.go文件中

package main

import "fmt"

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

func main() {
    var p3 people = people{"zq", 88, "合肥", "男"}
    var p4 people = p3
    fmt.Println("p3和p4是否相等:", p3 == p4)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
    "fmt"
)

在这一部分中,导入了标准库的 fmt 包,用于格式化输出。

部分 2 - 定义结构体类型

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

这里定义了一个名为 people 的结构体类型,它有四个字段:Name(字符串类型)、Age(整数类型)、Address(字符串类型)和 Gender(字符串类型)。

部分 3 - 主函数

func main() {
    // ...
}

这是程序的入口函数,所有的代码都将在 main 函数中执行。

部分 4 - 创建结构体变量并初始化

var p3 people = people{"zq", 88, "合肥", "男"}

在这一部分,创建了一个 people 类型的结构体变量 p3,并使用结构体字面值为其初始化字段值。最初的年龄是 88,地址是 "合肥"。

部分 5 - 赋值操作

var p4 people = p3

在这一部分,创建了另一个 people 类型的结构体变量 p4,并使用 p3 的值来初始化 p4。这是一个有效的赋值操作,将 p3 的值复制给了 p4

部分 6 - 比较结构体变量是否相等

fmt.Println("p3和p4是否相等:", p3 == p4)

最后,使用 == 运算符比较 p3p4 是否相等,然后使用 fmt.Println 打印结果。在这个示例中,p3p4 的所有字段值都相等,因此它们被认为是相等的,fmt.Println 打印出 true 表示它们相等。

运行上面代码

$ go run .\01-struct-03.go

p3和p4是否相等: true

五、结构体自定义方法

5.1 基本含义

在Go语言中,可以为自定义的结构体类型定义方法。方法是与特定类型关联的函数,可以通过这些方法来操作该类型的实例。结构体自定义方法允许我们为特定类型的结构体定义附属的函数。这些方法可以访问和操作结构体的字段,并且与该结构体类型相关联。

5.2 基本语法

在Go语言中,为结构体自定义方法的基本语法如下:

func (receiver ReceiverType) MethodName(parameters) ReturnType {
    // 方法的实现代码
}

上面参数说明:

  • func: 这是定义函数的关键字。

  • (receiver ReceiverType): 这是方法的接收者(receiver)声明,用于指定方法与哪种类型关联。ReceiverType 是接收者的类型,可以是结构体类型的值或指针。

5.3 示例说明

下面演示如何判断两个结构体变量是否相等,将下面代码粘贴到02-struct-00.go文件中

package main

import "fmt"

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

// 给自定义类型创建一个方法用来打印用户信息
// 小写字母开头
func (p *people) getinfo() string {
    return fmt.Sprintf("使用自定义方法获取当前用户名是:%s", p.Name)

}

// 大写字母开头
func (p *people) Eat(food string) {
    fmt.Printf("%s今天吃了%s\n", p.Name, food)

}

func main() {
    var p people = people{"zq", 18, "合肥", "男"}
    fmt.Printf("当前用户是:%s\n", p.Name)
    var p2 people = people{"zhanan", 66, "北京", "男"}
    info := p2.getinfo()
    fmt.Println(info)
    p2.Eat("宫保鸡丁")
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
    "fmt"
)

在这一部分中,导入了标准库的 fmt 包,用于格式化输出。

部分 2 - 定义自定义类型

type people struct {
    Name    string
    Age     int
    Address string
    Gender  string
}

在这里,定义了一个名为 people 的自定义类型,该类型具有四个字段:NameAgeAddressGender,用于表示人的信息。

部分 3 - 创建方法

func (p *people) getinfo() string {
    return fmt.Sprintf("使用自定义方法获取当前用户名是:%s", p.Name)
}

这是一个方法定义,它是一个小写字母开头的方法,附属于 people 类型。getinfo 方法接受一个 people 类型的指针 p,并返回一个格式化的字符串,包含用户名。

func (p *people) Eat(food string) {
    fmt.Printf("%s今天吃了%s\n", p.Name, food)
}

这是另一个方法定义,它是一个大写字母开头的方法,同样附属于 people 类型。Eat 方法接受一个字符串参数 food,然后使用 fmt.Printf 打印用户吃的食物以及用户名。

部分 4 - 主函数

func main() {
    var p people = people{"zq", 18, "合肥", "男"}
    fmt.Printf("当前用户是:%s\n", p.Name)

    var p2 people = people{"zhanan", 66, "北京", "男"}
    info := p2.getinfo()
    fmt.Println(info)
    p2.Eat("宫保鸡丁")
}

在主函数中,执行以下操作:

  • 创建了两个 people 类型的变量 pp2,并初始化它们的字段值。
  • 使用 fmt.Printf 打印了第一个人的用户名。
  • 调用 p2.getinfo() 方法,并将返回的字符串赋值给变量 info,然后使用 fmt.Println 打印了这个信息。
  • 调用 p2.Eat("宫保鸡丁") 方法,打印了用户吃的食物和用户名。

执行上面代码

$ go run .\02-struct-00.go
当前用户是:zq
使用自定义方法获取当前用户名是:zhanan
zhanan今天吃了宫保鸡丁

5.4 注意事项

在Go语言中,标识符(如函数、变量、方法等的名称)的首字母的大小写有着重要的含义,这影响了标识符在包内或包外的可见性。

  • 大写字母开头(例如 UpdateAge)的标识符是导出的(exported)。这意味着它们可以从包外部访问。如果你将一个标识符定义为大写字母开头,其他包就可以引用和使用它。

  • 小写字母开头(例如 updateAge)的标识符是未导出的(unexported)或私有的(private)。这意味着它们只能在同一个包内部使用,不能被其他包直接访问。

通常,我们应该根据标识符的使用情况来决定是否导出它。如果希望一个函数、方法或字段可以被其他包使用,那么首字母应该大写;如果只在包内部使用,首字母应该小写。

六、结构体方法接收者类型

Go 语言中的结构体方法接收者类型是指在定义结构体方法时,确定该方法是作用于结构体值还是结构体指针的类型。这个决定影响了方法对结构体数据的操作方式和副本的创建。

6.1 结构体方法接收者类型分类

在 Go 中,结构体方法的接收者类型可以分为两种:值类型接收者和指针类型接收者。

1.值类型接收者

在方法定义时,接收者是结构体的一个副本。这意味着方法对结构体的操作会在方法内部创建一个新的副本,不会影响原始结构体的值。

2.指针类型接收者(通常更常见,因为这允许在方法内部修改结构体的值)

在方法定义时,接收者是结构体的指针。这意味着方法可以修改原始结构体的值,而不是创建副本。通常,如果需要在方法内部修改结构体的字段值,可以使用指针类型接收者。

二者区别如下:

值类型接收者可以在方法中修改新变量的值后不会影响原有的值;而指针类型接收者在方法中修改新变量的值后会影响原有的值。

6.2 基本语法

1.值类型接收者

func (p Person) MethodName(parameters) ReturnType {
    // 方法的实现代码
}

参数说明:

  • MethodName: 这是自定义方法的名称,你可以自由选择方法的名称。

  • parameters: 这是方法的参数列表,可以包含零个或多个参数,用于接收传递给方法的值。

  • ReturnType: 这是方法的返回类型,表示方法执行后返回的数据类型。如果方法不返回任何值,可以使用 void 表示,或者直接省略返回类型。

2.指针类型接收者(通常更常见,因为这允许在方法内部修改结构体的值)

func (p *Person) MethodName(parameters) ReturnType {
    // 方法的实现代码
}

参数说明:

  • MethodName: 这是自定义方法的名称,你可以自由选择方法的名称。

  • parameters: 这是方法的参数列表,可以包含零个或多个参数,用于接收传递给方法的值。

  • ReturnType: 这是方法的返回类型,表示方法执行后返回的数据类型。如果方法不返回任何值,可以使用 void 表示,或者直接省略返回类型。

6.3 示例演示

下面演示值类型和指针类型的区别,将下面代码粘贴到03-struct-00.go文件中

package main

import "fmt"

type Rectangle struct {
    Width  int
    Height int
}

// 值类型接收者的方法,返回面积
func (r Rectangle) AreaValue() int {
    return r.Width * r.Height
}

// 指针类型接收者的方法,修改宽度
func (r *Rectangle) SetWidthPointer(newWidth int) {
    r.Width = newWidth
}

func main() {
    // 值类型接收者示例
    rectValue := Rectangle{Width: 5, Height: 10}
    areaValue := rectValue.AreaValue()
    fmt.Println("面积(值类型接收者示例说明):", areaValue)

    // 指针类型接收者示例
    rectPointer := &Rectangle{Width: 5, Height: 10}
    rectPointer.SetWidthPointer(8)
    areaPointer := rectPointer.AreaValue()
    fmt.Println("面积(指针类型接收者示例说明):", areaPointer)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
    "fmt"
)

在这一部分中,导入了标准库的 fmt 包,用于格式化输出。

部分 2 - 定义结构体类型

type Rectangle struct {
    Width  int
    Height int
}

这里定义了一个名为 Rectangle 的自定义结构体类型,它具有两个整数字段 WidthHeight,用于表示矩形的宽度和高度。

部分 3 - 值类型接收者的方法

// 值类型接收者的方法,返回面积
func (r Rectangle) AreaValue() int {
    return r.Width * r.Height
}

这是一个名为 AreaValue 的方法,它是一个值类型接收者方法。它接受一个 Rectangle 类型的值 r,并计算并返回矩形的面积,但不会修改原始矩形的值。

部分 4 - 指针类型接收者的方法

// 指针类型接收者的方法,修改宽度
func (r *Rectangle) SetWidthPointer(newWidth int) {
    r.Width = newWidth
}

这是一个名为 SetWidthPointer 的方法,它是一个指针类型接收者方法。它接受一个指向 Rectangle 类型的指针 r,并修改矩形的宽度。因为接收者是指针,所以它可以修改原始矩形的值。

部分 5 - 主函数

func main() {
    // 值类型接收者示例
    rectValue := Rectangle{Width: 5, Height: 10}
    areaValue := rectValue.AreaValue()
    fmt.Println("面积(值类型接收者示例说明):", areaValue)

    // 指针类型接收者示例
    rectPointer := &Rectangle{Width: 5, Height: 10}
    rectPointer.SetWidthPointer(8)
    areaPointer := rectPointer.AreaValue()
    fmt.Println("面积(指针类型接收者示例说明):", areaPointer)
}

这是程序的主函数。在这里,演示了值类型接收者和指针类型接收者的用法:

  • 创建了一个 Rectangle 类型的变量 rectValuerectPointer,分别用于值类型和指针类型接收者的示例。
  • 调用了 AreaValue 方法来计算矩形的面积,并输出结果。
  • 使用指针类型接收者的 SetWidthPointer 方法来修改矩形的宽度。
  • 最后,再次调用 AreaValue 方法来计算修改后的矩形的面积,并输出结果。

运行上面代码,证明了值类型接收者可以在方法中修改新变量的值后不会影响原有的值;而指针类型接收者在方法中修改新变量的值后会影响原有的值。

$ go run .\03-struct-00.go

面积(值类型接收者示例说明): 50
面积(指针类型接收者示例说明): 80

七、结构体嵌套

7.1 基本含义

当在 Go 语言中使用结构体时,结构体嵌套是一种有用的技术,它允许我们在一个结构体内部包含其他结构体。

7.2 基本语法

结构体嵌套的语法非常简单,我们只需在一个结构体内部嵌套其他结构体的类型。示例如下:

type OuterStruct struct {
    Field1  Type1
    Field2  Type2
    Inner   InnerStructType // 嵌套的结构体类型
}

type InnerStructType struct {
    FieldA  TypeA
    FieldB  TypeB
    // 其他字段...
}

上面参数说明:

  • OuterStruct:外部结构体,它包含了一个内部结构体 InnerStructType 作为其中一个字段。这个字段可以有任何名称,它用于引用内部结构体的实例。
  • InnerStructType:内部结构体,它是 OuterStruct 中的一个字段,但它本身也可以包含自己的字段。
  • Field1Field2FieldAFieldB 等字段:这些字段可以是任何数据类型,包括基本数据类型、自定义数据类型或其他结构体类型。

使用结构体嵌套的语法,我们可以创建层次化的数据结构,其中外部结构体包含一个或多个内部结构体,每个内部结构体可以有自己的字段,从而构建了更复杂的数据结构。这种方式有助于组织和管理数据,并支持面向对象编程中的复合关系。

7.3 结构体嵌套方式

结构体嵌套有两种方式:嵌入(嵌套)和匿名嵌套。

方式 1: 嵌入结构体

在这种方式下,明确地将一个结构体类型嵌套到另一个结构体中,并给它一个字段名称,以便在外部结构体中访问内部结构体的字段。

方式 2: 匿名嵌套结构体

在这种方式下,我们可以不指定字段名,直接嵌套结构体类型。这将使得内部结构体的字段名在外部结构体中可见。

7.4 示例说明

下面演示结构体嵌套的两种方式,将下面代码粘贴到04-struct-00.go文件中

package main

import "fmt"

// 定义名为Persion的结构体
type Persion struct {
    Name    string
    Age     int
    Address string
    Gender  string
    Mobile  Phone // 嵌套Phone结构体
}

// 定义名为Phone的结构体
type Phone struct {
    Mode  string
    Price float32
}

// 定义名为Info的结构体,匿名嵌套Person和Phone结构体
type Info struct {
    Persion // 嵌套Person结构体
    Phone   // 嵌套Phone结构体
}

func main() {
    var p1 Persion
    p1.Name = "zq"
    p1.Age = 18
    p1.Mobile.Mode = "HuaWei"
    p1.Mobile.Price = 8888.88
    fmt.Printf("用户:%s, 使用的手机是:%s\n", p1.Name, p1.Mobile.Mode)

    var i Info
    i.Name = "zq"
    i.Age = 22
    i.Mode = "Redmi"
    fmt.Printf("用户:%s, 使用的手机是:%s\n", i.Name, i.Mode)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
    "fmt"
)

在这一部分中,导入了标准库的 fmt 包,用于格式化输出。

部分 2 - 结构体定义

// 定义名为Persion的结构体
type Persion struct {
    Name    string
    Age     int
    Address string
    Gender  string
    Mobile  Phone // 嵌套Phone结构体
}

// 定义名为Phone的结构体
type Phone struct {
    Mode  string
    Price float32
}

这部分代码定义了两个结构体:PersonPhonePerson 结构体包含了姓名、年龄、地址、性别和一个嵌套的 Phone 结构体字段,用来表示用户的手机信息。Phone 结构体包含了手机型号和价格。

部分 3 - Info 结构体定义

// 定义名为Info的结构体,匿名嵌套Person和Phone结构体
type Info struct {
    Person // 匿名嵌套 Person 结构体
    Phone  // 匿名嵌套 Phone 结构体
}

这部分代码定义了一个名为 Info 的结构体,使用了匿名嵌套的方式,嵌套了 PersonPhone 结构体。这样,Info 结构体包含了 PersonPhone 结构体的所有字段。

部分 4 - 主函数 (main)

func main() {
    // 创建一个 Person 类型的变量 p1 并设置字段值
    var p1 Persion
    p1.Name = "zq"
    p1.Age = 18
    p1.Mobile.Mode = "HuaWei"
    p1.Mobile.Price = 8888.88

    // 打印用户的姓名和手机型号
    fmt.Printf("用户:%s, 使用的手机是:%s\n", p1.Name, p1.Mobile.Mode)

    // 创建一个 Info 类型的变量 i 并设置字段值
    var i Info
    i.Name = "zq"
    i.Age = 22
    i.Mode = "Redmi"

    // 打印用户的姓名和手机型号
    fmt.Printf("用户:%s, 使用的手机是:%s\n", i.Name, i.Mode)
}

这部分代码包含了 main 函数,是程序的入口点。在 main 函数中,创建了 p1 变量,它是 Person 类型,然后设置了 p1 的字段值。接着,你使用 fmt.Printf 打印了用户的姓名和手机型号。然后,创建了 i 变量,它是 Info 类型,设置了 i 的字段值,并再次使用 fmt.Printf 打印了用户的姓名和手机型号。

运行上面代码

$ go run .\04-struct-00.go

用户:zq, 使用的手机是:HuaWei
用户:zq, 使用的手机是:Redmi

7.5 注意事项

在 Go 中,如果结构体嵌套中的成员变量冲突(即两个嵌套的结构体具有相同的字段名),我们可以使用结构体字段的完全限定名称来解决冲突。可以理解成Linux中的绝对路径。

假设有以下两个结构体,它们都有一个名为 Name 的字段:

type Person struct {
    Name string
}

type Animal struct {
    Name string
}

此时如果我们将它们嵌套在一个结构体中,会出现冲突:

type Profile struct {
    Person
    Animal
}

现在,如果我们想访问 Profile 结构体中的 Name 字段,需要使用完全限定的名称,指定是哪个嵌套结构体的字段。例如,要访问 Person 结构体的 Name 字段,可以这样做:

var profile Profile
profile.Person.Name = "zq"