Go 学习笔记:struct

391 阅读8分钟

什么是 struct

struct 是一种用户自定义的数据类型,在 Go 语言中用于表示一个对象的属性和方法。它可以包含任意类型的字段,也可以定义自己的方法,在处理复杂数据结构时,struct 是非常有用的。

如何声明一个 struct

在 Go 语言中,可以使用 type 关键字来声明一个 struct。例如,声明一个名为 Person 的 struct,包含一个 name 字段和一个 age 字段,可以像这样写:

type Person struct {
    name string
    age  int
}

然后,可以使用该 struct 来声明一个变量,例如:

var p Person

这将创建一个名为 p 的 Person 变量,其中的 name 和 age 字段将被初始化为其类型的零值。也可以在声明变量时为其提供初始值,例如:

p := Person{name: "Alice", age: 30}

这将创建一个名为 p 的 Person 变量,其中的 name 字段将被初始化为 "Alice",age 字段将被初始化为 30。

需要注意的是,Go 语言中的 struct 是值传递,因此在传递 struct 变量时,会将其复制一份。如果需要在函数中修改 struct 变量的值,可以使用指针类型的 struct 变量,例如:

func changeName(p *Person, name string) {
   p.name = name
}

如何使用 struct 的指针

Go 使用结构体指针的语法与使用结构体的语法非常相似,只需要在结构体类型前面加上 * 符号即可。当我们需要在函数中修改结构体变量的值时,通常会使用结构体指针。

以下是使用结构体指针的示例代码:

type Person struct {
    Name string
    Age  int
}

func main() {
    p1 := Person{"Alice", 30}
    fmt.Println(p1) // 输出: {Alice 30}

    p2 := &p1
    p2.Name = "Bob"
    p2.Age = 20
    fmt.Println(p1) // 输出: {Bob 20}
}

在上面的示例代码中,我们首先声明了一个名为 Person 的结构体类型,其包含了 Name 和 Age 两个字段。然后我们使用结构体字面量创建了一个 Person 类型的变量 p1,其值为 {Alice, 30}。接着,我们声明了一个名为 p2 的结构体指针变量,并将其指向了 p1。由于 p2 是一个指针变量,我们可以通过 *p2 来访问 p2 指向的结构体变量。在上面的代码中,我们通过 p2.Name 和 p2.Age 来分别修改了 p1 的 Name 和 Age 字段的值。最后,我们输出了 p1 的值,可以看到其值已经被修改为 {Bob, 20}。

需要注意的是,当我们使用结构体指针调用结构体的方法时,Go 会自动将指针解引用并调用方法。因此,如果一个方法的接收器是一个指针类型,我们既可以使用结构体变量调用该方法,也可以使用该结构体变量的指针调用该方法。例如:

type Person struct {
    Name string
    Age  int
}

func (p *Person) SayHello() {
    fmt.Printf("Hello, my name is %s and I'm %d years old.\\n", p.Name, p.Age)
}

func main() {
    p1 := Person{"Alice", 30}
    p1.SayHello() // 输出: Hello, my name is Alice and I'm 30 years old.

    p2 := &p1
    p2.SayHello() // 输出: Hello, my name is Alice and I'm 30 years old.
}

在上面的示例代码中,我们定义了一个名为 SayHello 的方法,其接收器是一个指向 Person 类型的指针。然后我们声明了一个 Person 类型的变量 p1,并使用结构体字面量初始化了其值。接着,我们使用 p1 和 p1 的指针 p2 分别调用了 SayHello 方法,最终输出了相同的结果。

struct 嵌套与组合

在 Go 中,可以将一个 struct 嵌套在另一个 struct 中,从而创建一个更复杂的数据类型。嵌套的 struct 可以包含任意数量的字段,这些字段可以是任何类型的。

下面是一个示例,其中一个 struct 嵌套在另一个 struct 中:

type Address struct {
    Street  string
    City    string
    State   string
    ZipCode string
}

type Person struct {
    Name    string
    Age     int
    Address Address
}

在上面的示例中,Person 结构体中包含一个 Address 结构体。这个 Address 结构体有四个字段:Street、City、State 和 ZipCode。可以通过以下方式访问 Person 结构体中的 Address 结构体中的字段:

p := Person{
    Name: "Alice",
    Age: 30,
    Address: Address{
        Street:  "123 Main St",
        City:    "Anytown",
        State:   "CA",
        ZipCode: "12345",
    },
}

fmt.Println(p.Address.City) // Output: Anytown

除了嵌套 struct,Go 还支持 struct 的组合,组合是指将一个 struct 的字段添加到另一个 struct 中,从而扩展其功能,下面是一个示例:

type Person struct {
    Name string
    Age  int
}

type Employee struct {
    Person
    Salary float64
}

func main() {
    e := Employee{
        Person: Person{
            Name: "Alice",
            Age:  30,
        },
        Salary: 50000.0,
    }

    fmt.Println(e.Name) // Output: Alice
}

在上面的示例中,Employee 结构体包含一个 Person 结构体和一个 Salary 字段。可以通过 e.Name 访问 Person 结构体中的 Name 字段。

struct 方法和接收器

在 Go 语言中,struct 可以定义自己的方法,方法是一种可以与特定类型的值关联的函数。方法可以被定义在任何类型上,不仅仅是 struct 上。

方法的语法与函数类似,只是在 func 关键字和方法名之间增加了一个接收器。接收器可以是任何类型的值,但是通常是一个指向 struct 的指针。

下面是一个例子:

type Person struct {
    Name string
    Age  int
}

func (p *Person) SayHello() {
    fmt.Printf("Hello, my name is %s and I am %d years old\\n", p.Name, p.Age)
}

func main() {
    person := &Person{"John", 30}
    person.SayHello()
}

在这个例子中,我们定义了一个 Person 类型,然后定义了一个 SayHello 的方法。该方法的接收器是一个指向 Person 的指针。在 main 函数中,我们创建了一个指向 Person 类型的指针变量,并调用了该变量的 SayHello 方法。

输出应该类似于以下内容:

Hello, my name is John and I am 30 years old

使用方法的好处是可以将逻辑和数据封装在一起,这样可以使代码更加清晰和简洁。同时,方法还可以实现接口,从而使类型实现某些特定的行为。

接收器可以是一个指向 struct 的指针,也可以是一个 struct 的值。如果接收器是一个指针,那么方法可以修改 struct 的值。但如果接收器是一个值,那么方法不能修改 struct 的值。

struct 与 interface 有什么区别?

在 Go 语言中,struct 和 interface 是两个不同的概念。struct 是一种复合数据类型,用于组合不同类型的字段,可以认为是一种自定义的数据结构。interface 是一种抽象类型,定义了一组方法的集合,可以认为是一种协议或契约。

在使用上,struct 主要用于定义数据的结构和组织,可以通过访问其字段来获取和修改数据。而 interface 主要用于定义方法的行为和约束,可以通过实现其方法集合来表示实现了该接口的类型。

另外,struct 和 interface 在内存分配和性能上也有一些差异。由于 struct 存储的是具体数据,因此其内存分配和访问速度相对较快;而 interface 存储的是类型信息和方法表,因此其内存分配和访问速度相对较慢。此外,由于 interface 包含动态类型信息,因此在运行时需要进行类型断言或类型转换,可能会带来额外的开销和风险。

struct 有哪些应用场景?

在Go语言中,struct是一种非常常见的数据类型,它的应用场景非常广泛。下面是几个常见的应用场景:

数据库操作

在进行数据库操作时,经常需要定义一个数据结构来表示表中的一行数据。这个数据结构通常就是一个struct类型,它的字段对应着表中的各个列。

配置文件解析

在读取配置文件时,通常需要将配置信息存储到一个数据结构中,以便程序在运行时能够方便地访问这些配置信息。这个数据结构通常也是一个struct类型,它的字段对应着配置文件中的各个属性。

网络编程

在进行网络编程时,经常需要定义一个数据结构来表示传输的数据。这个数据结构通常也是一个struct类型,它的字段对应着数据包中的各个字段。

模板解析

在进行模板解析时,通常需要定义一个数据结构来表示模板中的各个变量。这个数据结构通常也是一个struct类型,它的字段对应着模板中的各个变量。

除了以上几个应用场景外,struct还可以用于表示任何复杂的数据结构,例如树、图等等。

总之,struct 是一个非常实用的数据类型,它可以用于表示任何复杂的数据结构,让程序更加清晰、易于维护和扩展。