Go struct
最后修改时间 2024 年 4 月 11 日
在本文中,我们将展示如何在 Golang 中使用结构体。
Struct
一个 struct 是一个用户定义的类型,它包含一组字段。它用于将相关数据组合成一个单一单元。Go 的 struct 可以与一个不包含继承特性的轻量级类进行比较。
Go struct 定义
Struct 使用 type 关键字进行定义。
type User struct {
name string
occupation string
age int
}
使用 type 关键字创建一个新类型。后面跟着类型名称(User)。struct 关键字表示我们正在创建一个 struct。在大括号内,我们有一系列字段。每个字段都有一个名称和一个类型。
Go 初始化 struct
我们将展示如何在 Go 中初始化 struct 类型。
u := User{"John Doe", "gardener", 34}
创建了一个新的 User struct。Struct 字段使用大括号内的值进行初始化。在这种情况下,字段的顺序是相关的。
u := User{
name: "John Doe",
occupation: "gardener",
age: 34,
}
我们可以同时提供字段名和值。在这种情况下,顺序并不重要。请注意,最后的逗号是必需的。
u := User{}
如果我们省略大括号中的值,它们将被初始化为零值。
Go struct 简单示例
下面是一个 Go struct 简单示例。
package main
import "fmt"
type User struct {
name string
occupation string
age int
}
func main() {
u := User{"John Doe", "gardener", 34}
fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation)
}
我们定义了一个包含三个字段的 User struct。
type User struct {
name string
occupation string
age int
}
我们声明了 User struct。
u := User{"John Doe", "gardener", 34}
我们初始化了 User struct。
fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation)
我们打印了 User struct 的内容。
$ go run main.go John Doe is 34 years old and he is a gardener
Go struct 访问字段
Struct 字段使用点运算符进行访问。
package main
import "fmt"
type User struct {
name string
occupation string
age int
}
func main() {
u := User{}
u.name = "John Doe"
u.occupation = "gardener"
u.age = 34
fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation)
}
我们创建了一个空的 User struct。我们使用点运算符初始化字段并读取它们。
Go 匿名 struct
在 Go 中可以创建匿名 struct。匿名 struct 没有名称。它们只被创建一次。
package main
import "fmt"
func main() {
u := struct {
name string
occupation string
age int
}{
name: "John Doe",
occupation: "gardener",
age: 34,
}
fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation)
}
匿名 struct 仅使用 struct 关键字创建。Struct 的声明后面跟着它的初始化。
Go 嵌套 struct
Go struct 可以嵌套。
package main
import "fmt"
type Address struct {
city string
country string
}
type User struct {
name string
age int
address Address
}
func main() {
p := User{
name: "John Doe",
age: 34,
address: Address{
city: "New York",
country: "USA",
},
}
fmt.Println("Name:", p.name)
fmt.Println("Age:", p.age)
fmt.Println("City:", p.address.city)
fmt.Println("Country:", p.address.country)
}
在代码示例中,Address struct 嵌套在 User struct 中。
fmt.Println("City:", p.address.city)
fmt.Println("Country:", p.address.country)
要访问嵌套 struct 的字段,我们首先使用点运算符访问内部 struct;然后访问相应的字段。
$ go run nested.go Name: John Doe Age: 34 City: New York Country: USA
Go struct 字段提升
嵌套匿名 struct 的字段会被提升;也就是说,它们在不引用嵌套 struct 的情况下即可访问。
package main
import "fmt"
type Address struct {
city string
country string
}
type User struct {
name string
age int
Address
}
func main() {
p := User{
name: "John Doe",
age: 34,
Address: Address{
city: "New York",
country: "USA",
},
}
fmt.Println("Name:", p.name)
fmt.Println("Age:", p.age)
fmt.Println("City:", p.city)
fmt.Println("Country:", p.country)
}
在代码示例中,我们有一个嵌套的 Address struct。
type User struct {
name string
age int
Address
}
User struct 包含一个嵌套的匿名 Address struct。该字段没有名称。
fmt.Println("City:", p.city)
fmt.Println("Country:", p.country)
city 和 country 字段被提升。它们可以直接通过父 struct 访问。
$ go run main.go Name: John Doe Age: 34 City: New York Country: USA
Go struct 函数字段
Struct 字段可以是函数。
package main
import "fmt"
type Info func(string, string, int) string
type User struct {
name string
occupation string
age int
info Info
}
func main() {
u := User{
name: "John Doe",
occupation: "gardener",
age: 34,
info: func(name string, occupation string, age int) string {
return fmt.Sprintf("%s is %d years old and he is a %s\n", name, age, occupation)
},
}
fmt.Printf(u.info(u.name, u.occupation, u.age))
}
在代码示例中,我们有 User struct。它的 info 字段是一个名为 Info 的函数。
Go struct 指针
可以使用 & 运算符或 new 关键字创建 struct 的指针。指针使用 * 运算符解引用。
package main
import "fmt"
type Point struct {
x int
y int
}
func main() {
p := Point{3, 4}
p_p := &p
(*p_p).x = 1
p_p.y = 2
fmt.Println(p)
}
在代码示例中,我们创建了一个 Point struct 的指针。
p_p := &p
& 运算符返回 Point 结构体的指针。
(*p_p).x = 1 p_p.y = 2
指针使用 * 运算符解引用。Go 也允许直接使用点运算符。
或者,我们可以使用 new 关键字创建 struct 的指针。
package main
import "fmt"
type User struct {
name string
occupation string
age int
}
func main() {
u := new(User)
u.name = "Richard Roe"
u.occupation = "driver"
u.age = 44
fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation)
}
该示例使用 new 关键字创建了一个新的 User struct 指针。
Go struct 构造函数
Go 中没有内置的构造函数。程序员有时会创建构造函数作为最佳实践。
package main
import "fmt"
type User struct {
name string
occupation string
age int
}
func newUser(name string, occupation string, age int) *User {
p := User{name, occupation, age}
return &p
}
func main() {
u := newUser("Richard Roe", "driver", 44)
fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation)
}
在代码示例中,我们有 newUser 构造函数,它创建新的 User struct。该函数返回新创建 struct 的指针。
Go struct 是值类型
Go struct 是值类型。当我们把一个 struct 变量赋值给另一个 struct 变量时,会创建一个 struct 的新副本。同样,当我们把一个 struct 传递给另一个函数时,函数会接收到 struct 的新副本。
package main
import "fmt"
type User struct {
name string
occupation string
age int
}
func main() {
u1 := User{"John Doe", "gardener", 34}
u2 := u1
u2.name = "Richard Roe"
u2.occupation = "driver"
u2.age = 44
fmt.Printf("%s is %d years old and he is a %s\n", u1.name, u1.age, u1.occupation)
fmt.Printf("%s is %d years old and he is a %s\n", u2.name, u2.age, u2.occupation)
}
在代码示例中,我们将一个 struct 赋给了另一个 struct。更改新 struct 的字段不会影响原始 struct。
$ go run main.go John Doe is 34 years old and he is a gardener Richard Roe is 44 years old and he is a driver
这两个 struct 是独立的实体。
比较 Go struct
如果 Go struct 的所有对应字段都相等,那么它们就相等。
package main
import "fmt"
type Point struct {
x int
y int
}
func main() {
p1 := Point{3, 4}
p2 := Point{3, 4}
if p1 == p2 {
fmt.Println("The structs are equal")
} else {
fmt.Println("The structs are not equal")
}
}
在代码示例中,我们比较了两个 Point struct。
$ go run main.go The structs are equal
Go 导出 struct
以大写字母开头的命名 struct 会被导出,并且可以在其包外访问。同样,以大写字母开头的 struct 字段也会被导出。以小写字母开头的 struct 名称和字段仅在其包内可见。
$ go mod init exporting
我们使用 go mod init 命令创建一个新的 Go 模块。
go.mod main └── main.go model ├── address.go └── user.go
这是项目结构。
package model
type User struct {
Name string
Occupation string
age int
}
User struct 的 Name 和 Occupation 字段被导出,而 age 字段未被导出。
package model
type address struct {
city string
country string
}
address struct 未被导出。我们无法在 main.go 文件中引用它。
package main
import (
"exporting/model"
"fmt"
)
func main() {
u := model.User{Name: "John Doe", Occupation: "gardener"}
fmt.Printf("%s is a %s\n", u.Name, u.Occupation)
}
在 main.go 文件中,我们从 exporting/model 包导入了 Name 和 Occupation 字段。
Go 创建 struct 切片
在下面的示例中,我们创建了一个 struct 切片。
package main
import "fmt"
type User struct {
name string
occupation string
country string
}
func main() {
users := []User{}
users = append(users, User{"John Doe", "gardener", "USA"})
users = append(users, User{"Roger Roe", "driver", "UK"})
users = append(users, User{"Paul Smith", "programmer", "Canada"})
users = append(users, User{"Lucia Mala", "teacher", "Slovakia"})
users = append(users, User{"Patrick Connor", "shopkeeper", "USA"})
users = append(users, User{"Tim Welson", "programmer", "Canada"})
users = append(users, User{"Tomas Smutny", "programmer", "Slovakia"})
for _, user := range users {
fmt.Println(user)
}
}
定义了一个 User 类型。然后我们创建了一个空的 User struct 切片。我们使用 append 向切片添加元素。
Go 过滤 struct 切片
在下一个示例中,我们过滤了一个 Go 结构体切片。
package main
import "fmt"
type User struct {
name string
occupation string
country string
}
func main() {
users := []User{
{"John Doe", "gardener", "USA"},
{"Roger Roe", "driver", "UK"},
{"Paul Smith", "programmer", "Canada"},
{"Lucia Mala", "teacher", "Slovakia"},
{"Patrick Connor", "shopkeeper", "USA"},
{"Tim Welson", "programmer", "Canada"},
{"Tomas Smutny", "programmer", "Slovakia"},
}
var programmers []User
for _, user := range users {
if isProgrammer(user) {
programmers = append(programmers, user)
}
}
fmt.Println("Programmers:")
for _, u := range programmers {
fmt.Println(u)
}
}
func isProgrammer(user User) bool {
return user.occupation == "programmer"
}
在代码示例中,我们定义了一个用户切片。我们创建了一个只包含程序员的新切片。
type User struct {
name string
occupation string
country string
}
User struct 有三个字段。
users := []User{
{"John Doe", "gardener", "USA"},
{"Roger Roe", "driver", "UK"},
{"Paul Smith", "programmer", "Canada"},
{"Lucia Mala", "teacher", "Slovakia"},
{"Patrick Connor", "shopkeeper", "USA"},
{"Tim Welson", "programmer", "Canada"},
{"Tomas Smutny", "programmer", "Slovakia"},
}
这是原始的 User 结构体切片。
var programmers []User
过滤后的用户/程序员存储在 programmers 切片中。
for _, user := range users {
if isProgrammer(user) {
programmers = append(programmers, user)
}
}
我们遍历 users 切片,如果用户满足 isProgrammer 断言,则将当前用户添加到 programmers 切片中。
func isProgrammer(user User) bool {
return user.occupation == "programmer"
}
IsProgrammer 断言对于所有 occupation 字段等于 "programmer" 的用户都返回 true。
$ go run main.go
Programmers:
{Paul Smith programmer Canada}
{Tim Welson programmer Canada}
{Tomas Smutny programmer Slovakia}
来源
The Go Programming Language Specification
在本文中,我们介绍了 Golang 中的 struct。