Golang结构体字段应该使用指针吗

Golang结构体字段应该使用指针吗 你好,

我在网上尝试查找相关信息但未能找到,所以在此提问。

为什么有人会使用如下所示的指针字段?为什么应该优先选择其中一种方式?例如:内存、性能、比较选项等方面……

type Request struct {
	Name              *string `json:"name"`
	No                *uint16 `json:"no"`
	Address           *string `json:"address"`
	CreatedAt         *string `json:"created_at"`
	IsActive          *bool   `json:"is_active"`
}
type Request struct {
	Name              string `json:"name"`
	No                uint16 `json:"no"`
	Address           string `json:"address"`
	CreatedAt         string `json:"created_at"`
	IsActive          bool   `json:"is_active"`
}
var r Request
d := json.NewDecoder(httprequest.body)
d.DisallowUnknownFields()
e := d.Decode(&r)

更多关于Golang结构体字段应该使用指针吗的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

通常,使用指针可以避免复制数据,但也会暴露可变性。

对于可JSON序列化的字段(以及许多其他序列化格式),它也标注了“字段的可选性”。

更多关于Golang结构体字段应该使用指针吗的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


除了 @NobbZ 的回答,在某些情况下,你需要区分零值和 null/nil。

假设你有一个名为 replied 的布尔字段。

type Answer struct{
    ID int  `json:"id"`
    Replied bool `json:"replied"`
}
// data:`{"id": 1}
// data:`{"id": 1, "replied": false}

当你将上述 JSON 字符串反序列化到一个 Answer 对象时,两者的 replied 字段最终都会是 false。当你持有这个具体对象时,你将无法知道 replied 为 false 的原因:是因为用户尚未回复,还是因为 JSON 中根本没有 replied 这个键。为了克服这种歧义,你需要将 replied 字段改为布尔指针,这样对于 {"id": 1}replied 就会是 nil。

Go 零值

在Golang中,结构体字段使用指针主要适用于以下场景:

  1. 区分零值和未设置值:当JSON反序列化时,指针字段可以区分字段是未设置(nil)还是显式设置为零值(空字符串、0、false等)
type Request struct {
    Name     *string `json:"name"`  // nil表示未设置,空字符串指针表示显式设置为空
    IsActive *bool   `json:"is_active"`
}

// 示例:区分JSON中缺少字段和字段值为false
json1 := `{"is_active": false}`  // IsActive指向false
json2 := `{}`                    // IsActive为nil
  1. 可选字段:当某些字段在业务逻辑中是可选的,使用指针可以更清晰地表达意图
type User struct {
    ID        int     `json:"id"`
    Email     string  `json:"email"`
    Phone     *string `json:"phone,omitempty"`  // 可选字段
    Address   *string `json:"address,omitempty"` // 可选字段
}
  1. 内存优化:对于大型结构体或频繁传递的结构体,使用指针可以减少复制开销
type LargeStruct struct {
    Data     *[]byte    `json:"data"`     // 避免大数据复制
    Metadata *Meta      `json:"metadata"` // 嵌套结构体指针
}

// 传递指针避免复制
func process(s *LargeStruct) {
    // 直接操作原数据
}
  1. 性能考虑:指针字段在特定场景下可能更高效,但需要权衡GC压力
// 值类型字段 - 每次赋值都会复制
type ValueFields struct {
    A string
    B string
    C string
}

// 指针字段 - 只复制指针(8字节)
type PointerFields struct {
    A *string
    B *string
    C *string
}
  1. JSON处理中的空值处理
type Request struct {
    Name     *string `json:"name"`
    Age      *int    `json:"age"`
}

func main() {
    jsonStr := `{"name": null, "age": 25}`
    var req Request
    json.Unmarshal([]byte(jsonStr), &req)
    
    // req.Name为nil(显式null)
    // req.Age指向25
}

对于你的具体示例,如果API需要区分"字段未提供"和"字段显式设置为空值",那么指针字段是必要的。否则,使用值类型字段更简单且减少GC压力。

// 如果需要区分未设置和零值
type Request struct {
    Name      *string `json:"name"`      // nil=未设置,空字符串指针=显式清空
    IsActive  *bool   `json:"is_active"` // nil=未设置,false指针=显式设为false
}

// 如果不需要区分,使用值类型更简洁
type Request struct {
    Name      string `json:"name"`      // 空字符串可能是未设置或显式清空
    IsActive  bool   `json:"is_active"` // false可能是未设置或显式设为false
}

选择依据:

  • 需要区分零值和未设置 → 使用指针
  • 字段可选且可能很大 → 考虑使用指针
  • 简单场景且不需要区分 → 使用值类型
  • 频繁创建和销毁的小对象 → 优先使用值类型减少GC压力
回到顶部