Go语言基础语法 - 05

75 阅读6分钟

结构体

在 Golang 中,结构体(struct)是一种复合数据类型,它将多个不同类型的字段组合在一起。结构体用于表示具有多个属性的复杂数据类型。

package main

import "fmt"

type user struct {
    name     string
    password string
}

func main() {
    a := user{name: "wang", password: "1024"}
    b := user{"wang", "1024"}
    c := user{name: "wang"}
    c.password = "1024"
    var d user
    d.name = "wang"
    d.password = "1024"

    fmt.Println(a, b, c, d)                 // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
    fmt.Println(checkPassword(a, "haha"))   // false
    fmt.Println(checkPassword2(&a, "haha")) // false
}

func checkPassword(u user, password string) bool {
    return u.password == password
}

func checkPassword2(u *user, password string) bool {
    return u.password == password
}

结构体定义

type user struct {
    name     string
    password string
}
  • 定义结构体: 使用 type 关键字定义一个名为 user 的结构体。
  • 字段: 结构体包含两个字段:name 和 password,它们的类型都是 string

结构体初始化

a := user{name: "wang", password: "1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}
c.password = "1024"
var d user
d.name = "wang"
d.password = "1024"
  • 完整初始化: 使用键值对的方式初始化结构体 a,指定所有字段的初始值。
  • 简写初始化: 直接提供字段值初始化结构体 b,字段顺序必须与定义顺序一致。
  • 部分初始化: 使用键值对的方式初始化结构体 c,只初始化部分字段,未初始化的字段会使用零值。
  • 零值初始化: 声明结构体变量 d,未初始化的字段会使用零值,然后逐个字段赋值。

结构体指针

func checkPassword2(u *user, password string) bool {
    return u.password == password
}
  • 指针参数: 函数 checkPassword2 接受一个 user 结构体的指针参数 u
  • 指针传递: 通过传递指针,可以在函数中修改结构体的字段,避免大结构体的拷贝开销。

在 main 函数中调用 checkPassword2 函数:

fmt.Println(checkPassword2(&a, "haha")) // false
  • 传递指针: 使用 & 获取结构体变量 a 的地址,传递给函数 checkPassword2

总结

  • 结构体定义: 结构体是带类型字段的集合,用于表示复杂数据类型。
  • 结构体初始化: 可以使用键值对或简写方式初始化结构体,未初始化的字段会使用零值。
  • 结构体指针: 通过传递结构体指针,可以在函数中修改结构体的字段,避免大结构体的拷贝开销。

结构体方法

在 Golang 中,可以为结构体定义方法,这类似于其他编程语言中的类成员函数。通过为结构体定义方法,可以更方便地操作结构体实例。

package main

import "fmt"

type user struct {
    name     string
    password string
}

// checkPassword 方法用于检查用户密码是否匹配
func (u user) checkPassword(password string) bool {
    return u.password == password
}

// resetPassword 方法用于重置用户密码
func (u *user) resetPassword(password string) {
    u.password = password
}

func main() {
    a := user{name: "wang", password: "1024"}
    a.resetPassword("2048")
    fmt.Println(a.checkPassword("2048")) // true
}

定义结构体方法

在 Golang 中,可以为结构体定义方法。方法的定义与普通函数类似,但需要在函数名称前面加上接收者(receiver)。接收者可以是值类型或指针类型。

func (u user) checkPassword(password stringbool {
    return u.password == password
}
  • 接收者u user 是方法的接收者,表示该方法属于 user 结构体。
  • 调用方法: 可以通过结构体实例调用该方法,例如 a.checkPassword("2048")

值接收者与指针接收者

在实现结构体方法时,可以选择使用值接收者或指针接收者。它们的区别在于是否可以修改结构体实例的字段。

值接收者
func (u user) checkPassword(password stringbool {
    return u.password == password
}
  • 值接收者: 方法接收者是结构体的值副本,对副本的修改不会影响原始结构体实例。
  • 用途: 适用于不需要修改结构体实例的场景。
指针接收者
func (u *user) resetPassword(password string) {
    u.password = password
}
  • 指针接收者: 方法接收者是结构体的指针,可以修改结构体实例的字段。
  • 用途: 适用于需要修改结构体实例的场景。

示例代码中的方法

在示例代码中,定义了两个方法:checkPassword 和 resetPassword

func (u user) checkPassword(password stringbool {
    return u.password == password
}
  • checkPassword 方法: 使用值接收者,不会修改结构体实例。
func (u *user) resetPassword(password string) {
    u.password = password
}
  • resetPassword 方法: 使用指针接收者,可以修改结构体实例的字段。

在 main 函数中调用这两个方法:

func main() {
    a := user{name: "wang", password: "1024"}
    a.resetPassword("2048")
    fmt.Println(a.checkPassword("2048")) // true
}
  • 调用 resetPassword 方法: 修改结构体实例 a 的 password 字段。
  • 调用 checkPassword 方法: 检查修改后的密码是否匹配。

总结

  • 结构体方法: 可以为结构体定义方法,类似于类的成员函数。
  • 接收者: 方法的接收者可以是值类型或指针类型。
  • 值接收者: 方法接收者是结构体的值副本,对副本的修改不会影响原始结构体实例。
  • 指针接收者: 方法接收者是结构体的指针,可以修改结构体实例的字段。

错误处理

在 Golang 中,错误处理的惯用做法是使用一个单独的返回值来传递错误信息。与 Java 等语言使用的异常机制不同,Golang 的错误处理方式更加直接和清晰,可以通过简单的 if-else 语句来处理错误。

package main

import (
    "errors"
    "fmt"
)

type user struct {
    name     string
    password string
}

func findUser(users []user, name string) (v *user, err error) {
    for _, u := range users {
        if u.name == name {
            return &u, nil
        }
    }
    return nil, errors.New("not found")
}

func main() {
    u, err := findUser([]user{{"wang", "1024"}}, "wang")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(u.name) // wang

    if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
        fmt.Println(err) // not found
        return
    } else {
        fmt.Println(u.name)
    }
}
错误类型

在 Golang 中,错误类型是内置的 error 接口。可以通过实现 Error 方法来创建自定义错误类型。标准库中的 errors 包提供了创建错误的简单方法。

import "errors"
  • 创建错误: 使用 errors.New 函数创建一个新的错误。
函数返回错误

在函数定义中,可以在返回值类型列表中添加 error 类型,表示该函数可能会返回错误。

func findUser(users []user, name string) (v *user, err error) {
    for _, u := range users {
        if u.name == name {
            return &u, nil
        }
    }
    return nil, errors.New("not found")
}
  • 返回值: 函数 findUser 返回两个值:一个是找到的用户指针,另一个是错误信息。
  • 返回错误: 如果找到用户,则返回用户指针和 nil;如果未找到用户,则返回 nil 和错误信息。
错误处理

在调用返回错误的函数时,可以使用 if-else 语句来处理错误。

func main() {
    u, err := findUser([]user{{"wang""1024"}}, "wang")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(u.name) // wang
    if u, err := findUser([]user{{"wang""1024"}}, "li"); err != nil {
        fmt.Println(err) // not found
        return
    } else {
        fmt.Println(u.name)
    }
}
  • 处理错误: 调用 findUser 函数后,检查返回的错误。如果有错误,则打印错误信息并返回;如果没有错误,则继续处理返回的结果。

总结

  • 错误类型: Golang 使用内置的 error 接口来表示错误。
  • 函数返回错误: 在函数返回值类型列表中添加 error 类型,表示该函数可能会返回错误。
  • 错误处理: 使用 if-else 语句来处理函数返回的错误,清晰地知道哪个函数返回了错误,并进行相应的处理。