如何判断Golang代码是否适合使用指针_Golang设计决策参考

该用 *T 而不是 T 的核心依据是「语义是否需要共享可变状态」:需修改入参、字段含切片/map/大数组、或类型不可拷贝(如 sync.Mutex)时才用指针,否则默认传值更安全易推理。

什么时候该用 *T 而不是 T

核心判断依据不是“性能”,而是「语义是否需要共享可变状态」。如果函数要修改入参、或结构体字段较大(比如含切片、map、大数组)、或类型本身不允许拷贝(如 sync.Mutex),才考虑指针。否则默认传值更安全、更易推理。

  • sync.Mutex 必须取地址:直接传值会复制锁,失去互斥意义
  • 结构体字段含 []bytemap[string]int 时,传指针避免底层数据重复分配
  • 函数签名暴露 *T 意味着调用方需承担「该值可能被修改」的责任,这是契约的一部分

哪些常见类型传指针反而有害?

小而固定大小的类型(如 intboolstringtime.Time)传指针通常没收益,还增加 nil 风险和 GC 压力。尤其是 string —— 它本身是只读 header(16 字节),传值开销极小,但传 *string 后必须检查是否为 nil,且无法保证底层字节数组不被意外修改(虽然 string 不可变,但指针解引用后仍可能引发误用)。

  • stringint64error(接口,但多数实现很小)优先传值
  • 返回 *T 时,若 T 是小类型,不如直接返回 T + error
  • 方法接收者用

    *T 仅当方法需修改字段;否则 T 更清晰

如何快速识别指针滥用?

看代码里是否频繁出现 nil 检查、if p == nilpanic("nil pointer dereference") 这类防御逻辑。这往往说明本该用值语义的地方强行用了指针,把控制流复杂度推给了调用方。

  • 构造函数返回 *T 却没做非空校验 → 调用方极易 panic
  • 参数是 *[]int**string → 几乎总是设计错误,应重构为返回新值或使用切片/接口
  • HTTP handler 中把 *http.Request 当普通参数传(实际框架已保证非 nil)→ 多余且误导
func process(data *string) { // ❌ 无必要
    if data == nil {
        return
    }
    // ...
}

func process(data string) { // ✅ 更直接,调用方无需担心 nil
    // ...
}

真正难的是权衡「语义清晰性」和「运行时成本」——多数 Go 项目卡点不在指针与否,而在是否让调用方一眼看出谁拥有数据、谁负责生命周期、哪些操作是副作用。把这点想清楚了,指针只是自然结果,不是优化手段。