Golang中如何表示不存在的值?

Golang中如何表示不存在的值? 在支持空值的语言中,我会使用空值来表示某个值不可用:

class User {
  age: Int? = null
}

在 Go 中,等效的做法是什么?我应该使用布尔标志吗?

type User struct {
 isAgePresent bool
 age int
}

如果答案是肯定的,那么有哪些指导原则、命名约定等。

4 回复

我会使用指针。

type User struct {
    Age *int
}

指针的零值是 nil

更多关于Golang中如何表示不存在的值?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


有三种常见的做法来表示缺少一个值。

  • 零值
  • nil指针
  • 布尔值false

如果零值是一个有效的值,那么非零的零值方法是最简单的,因为它不需要任何额外操作,但如果你的类型的零值是一个有效值,这种方法就不可行。

nil指针方法可行,但潜在的缺点是堆分配。

布尔值方法用于零值是有效值但又不一定希望进行堆分配的情况。包括 database/sql 在内的各种库都使用这种方法。这种方法的好处是清晰明了,代价是代码冗长。也就是说,读者可以清楚地知道该值是否有效或存在,而无需根据指针是否为nil或值是否为零值来推断。

// 示例代码:使用布尔值表示可选字段
type Result struct {
    Value int
    Valid bool // 指示Value是否有效
}

除了已有的精彩回复外,我想补充一点:你也可以使用 database/sql 包中较新的泛型 Null 类型:

// Null 表示一个可能为空的值。
// Null 实现了 [Scanner] 接口,因此
// 它可以被用作扫描目标:
//
//	var s Null[string]
//	err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
//	...
//	if s.Valid {
//	   // 使用 s.V
//	} else {
//	   // NULL 值
//	}
type Null[T any] struct {
	V     T
	Valid bool
}

因此,你可以将你的用户结构体重构为类似这样:

type User struct {
	Age sql.Null[int]
}

这支持从 SQL 进行扫描。唯一的缺点是:如果我采用这种方法,并且 JSON 是我的最终输出目标,我通常会实现一个自定义的 JSON 编组器,因为在客户端我只想要一个可为空的整数。出于这个原因,我倾向于直接使用指针。或者——在最佳情况下,使用零值,其中 0 不是一个有效值(例如 ID 等)。

在Go中,处理不存在值的主要方法有以下几种:

1. 使用指针(最接近其他语言的空值)

type User struct {
    age *int
}

func main() {
    user := User{}
    
    // 设置值
    age := 25
    user.age = &age
    
    // 检查是否存在
    if user.age != nil {
        fmt.Printf("Age: %d\n", *user.age)
    } else {
        fmt.Println("Age not set")
    }
}

2. 使用零值和布尔标志(如你提到的)

type User struct {
    ageSet bool
    age    int
}

func (u *User) SetAge(age int) {
    u.age = age
    u.ageSet = true
}

func (u *User) Age() (int, bool) {
    return u.age, u.ageSet
}

3. 使用自定义类型和错误处理

type OptionalInt struct {
    value int
    valid bool
}

func (o OptionalInt) Value() (int, error) {
    if !o.valid {
        return 0, errors.New("value not set")
    }
    return o.value, nil
}

func (o *OptionalInt) Set(value int) {
    o.value = value
    o.valid = true
}

4. 使用函数返回多个值(Go的惯用方式)

type User struct {
    age int
}

func (u *User) GetAge() (int, bool) {
    // 假设0表示未设置
    return u.age, u.age != 0
}

// 或者使用错误
func (u *User) GetAgeWithError() (int, error) {
    if u.age == 0 {
        return 0, errors.New("age not set")
    }
    return u.age, nil
}

命名约定

  • 布尔标志通常使用 hasXxx, isXxxSet, xxxPresent 等前缀
  • 指针方式更简洁,但需要注意内存管理
  • 对于数据库操作,可以使用 sql.NullInt64 等类型

选择哪种方式取决于具体场景:

  • 指针方式适合需要明确区分零值和未设置值的场景
  • 布尔标志方式更显式,但代码更冗长
  • 多返回值是Go的惯用模式,特别适合函数返回值
回到顶部