在 Go 里,这事儿得直说一句:函数参数本身是没有“默认值”这个语法的 😄
不像 Python / JS 那样能直接写。
不过别急,Go 社区早就把几种“官方认可的姿势”玩明白了,按使用频率给你捋一遍。
Go 的哲学:
“调用方传零值,我来决定要不要用默认值。”
func Connect(timeout time.Duration) {
if timeout == 0 {
timeout = 5 * time.Second
}
// ...
}
调用:
Connect(0) // 用默认 5s
Connect(10 * time.Second) // 自定义
✔ 简单
✔ 读代码就懂
❌ 只能区分“零值 vs 非零值”
适合 参数少、顺序固定 的情况:
func Connect(addr string, timeout ...time.Duration) {
t := 5 * time.Second
if len(timeout) > 0 {
t = timeout[0]
}
}
调用:
Connect("localhost")
Connect("localhost", 10*time.Second)
✔ 调用体验接近“默认参数”
❌ 参数一多就开始灾难
❌ 语义不够清晰
这是 Go 里最“正统”的方案 👑
type ConnectOptions struct {
Timeout time.Duration
Retries int
}
func Connect(addr string, opt *ConnectOptions) {
opts := ConnectOptions{
Timeout: 5 * time.Second,
Retries: 3,
}
if opt != nil {
if opt.Timeout != 0 {
opts.Timeout = opt.Timeout
}
if opt.Retries != 0 {
opts.Retries = opt.Retries
}
}
}
调用:
Connect("localhost", nil)
Connect("localhost", &ConnectOptions{
Timeout: 10 * time.Second,
})
✔ 参数语义清晰
✔ 可扩展、稳定
✔ 非常适合公共 API / SDK
你现在写的 WebSocket / Session / Pipeline 代码——强烈推荐这个。
type Option func(*Config)
type Config struct {
Timeout time.Duration
Debug bool
}
func WithTimeout(d time.Duration) Option {
return func(c *Config) {
c.Timeout = d
}
}
func WithDebug() Option {
return func(c *Config) {
c.Debug = true
}
}
func NewClient(opts ...Option) *Client {
cfg := &Config{
Timeout: 5 * time.Second,
Debug: false,
}
for _, opt := range opts {
opt(cfg)
}
return &Client{cfg: cfg}
}
调用:
NewClient()
NewClient(WithTimeout(10*time.Second))
NewClient(WithTimeout(10*time.Second), WithDebug())
✔ 默认值清晰
✔ 参数可选、顺序无关
✔ 工业级写法(k8s / grpc / zap 全在用)
| 场景 | 推荐方案 |
|---|---|
| 参数 1–2 个、内部函数 | 零值兜底 |
| 简单工具函数 | 可变参数 |
| 对外 API / SDK | struct options |
| 长期维护、可扩展 | Functional Options ⭐ |
如果你愿意,我可以 直接用你现在的 Session / Client / WebSocket 代码,帮你改成一套 最地道的 Functional Options 写法,包括默认值、线程安全那种。