如何在Golang中使用结构

Gostruct教程展示了如何在Golang中使用结构。

$ go version
go version go1.18.1 linux/amd64

我们使用Go版本1.18。

结构

struct是包含字段集合的用户定义类型。它用于将相关数据分组以形成一个单元。Go结构可以比作没有继承特性的轻量级类。

Go结构体定义

结构是用type关键字定义的。

type User struct {
    name       string
    occupation string
    age        int
}

使用type关键字创建新类型。它后面是类型的名称(用户)。struct关键字表示我们正在创建一个结构。在大括号内,我们有一个字段列表。每个字段都有名称和类型。

去初始化结构

我们展示了如何在Go中初始化结构类型。

u := User{"John Doe", "gardener", 34}

创建了一个新的用户结构。结构字段使用大括号之间提供的值进行初始化。在这种情况下,字段的顺序是相关的。

u := User{
    name:       "John Doe",
    occupation: "gardener",
    age:        34,
}

我们可以同时提供字段名称和值。在这种情况下,顺序并不重要。请注意,最后一个逗号是必需的。

u := User{}

如果我们省略大括号中的值,它们将被初始化为零值。

Go结构简单示例

下面是一个Gostruct的简单例子。

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结构。

type User struct {
    name       string
    occupation string
    age        int
}

我们声明了User结构。

u := User{"John Doe", "gardener", 34}

我们初始化User结构。

fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation)

我们打印User结构的内容。

$ go run main.go
John Doe is 34 years old and he is a gardener

Go结构访问字段

使用点运算符访问结构字段。

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结构。我们用值初始化字段并使用点运算符读取它们。

去匿名结构

可以在Go中创建匿名结构。匿名结构没有名字。它们只创建一次。

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关键字创建。结构的声明之后是它的初始化。

Go嵌套结构

Go结构可以嵌套。

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结构嵌套在User结构中。

fmt.Println("City:", p.address.city)
fmt.Println("Country:", p.address.country)

要访问嵌套结构的字段,我们首先使用点运算符访问内部结构;然后我们访问相应的字段。

$ go run nested.go
Name: John Doe
Age: 34
City: New York
Country: USA

Gostructpromotedfields

提升了嵌套匿名结构的字段;也就是说,无需引用嵌套结构即可访问它们。

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结构。

type User struct {
    name string
    age  int
    Address
}

User结构有一个嵌套的匿名Address结构。该字段没有名称。

fmt.Println("City:", p.city)
fmt.Println("Country:", p.country)

citycountry字段被提升。它们通过直接引用父结构来访问。

$ go run main.go
Name: John Doe
Age: 34
City: New York
Country: USA

Go结构函数字段

结构字段可以是函数。

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结构。它的info字段是一个名为Info的函数。

Go结构指针

可以使用&运算符或new关键字创建指向结构的指针。使用*运算符取消引用指针。

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结构的指针。

p_p := &p

&运算符返回指向Point结构的指针。

(*p_p).x = 1
p_p.y = 2

指针被*运算符取消引用。Go还允许直接使用点运算符。

或者,我们可以使用new关键字创建一个指向结构的指针。

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结构的新指针。

Go结构构造函数

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结构。该函数返回指向新创建的结构的指针。

Gostruct是一个值类型

Go结构体是值类型。当我们将一个结构变量分配给另一个结构变量时,会创建该结构的一个新副本。同样,当我们将一个结构传递给另一个函数时,该函数会收到该结构的一个新副本。

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)
}

在代码示例中,我们将一个结构分配给另一个结构。更改新结构的字段不会影响原始结构。

$ 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

这两个结构是不同的实体。

比较Go结构

如果所有相应的字段都相等,则Go结构相等。

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结构。

$ go run main.go
The structs are equal

去导出结构

以大写字母开头的命名结构被导出并可从其包外部访问。同样,导出以大写字母开头的结构字段。以小写字母开头的结构名称和字段仅在其包内可见。

$ go mod init exporting

我们使用gomodinit命令创建一个新的Go模块。

go.mod
main
└── main.go
model
├── address.go
└── user.go

这是项目结构。

package model

type User struct {
    Name       string
    Occupation string
    age        int
}

User结构的NameOccupation字段被导出,而age字段则没有。

package model

type address struct {
    city    string
    country string
}

address结构未导出。我们不能在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导入NameOccupation字段包。

去创建一个结构片

在下面的示例中,我们创建了一个结构切片。

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类型被定义。然后我们创建一个空的Userstruct切片。我们使用append向切片添加元素。

过滤结构切片

在下一个例子中,我们过滤了一段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结构具有三个字段。

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

过滤后的用户/程序员存储在程序员切片中。

for _, user := range users {

    if isProgrammer(user) {
        programmers = append(programmers, user)
    }
}

我们遍历users切片,仅当用户满足isProgrammer谓词时,才将当前用户添加到programmers切片。

func isProgrammer(user User) bool {

    return user.occupation == "programmer"
}

对于occupation字段等于“程序员”的所有用户,IsProgrammer谓词返回true。

$ go run main.go
Programmers:
{Paul Smith programmer Canada}
{Tim Welson programmer Canada}
{Tomas Smutny programmer Slovakia}

在本教程中,我们介绍了Golang中的结构。

列出所有Go教程。

赞(0) 打赏

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏