Go Style 代码风格规范

Golang 代码 编程规范

Go 代码应该以最简单的方式编写,以实现其目标,无论是在行为还是性能方面。在 Google Go 代码库中,简单代码:

  • 从上到下都易于阅读;
  • 不假设你已经知道它在做什么;
  • 不假设你可以记住所有前面的代码;
  • 没有不必要的抽象层次;
  • 没有能引起关注的世俗名称;
  • 让开发者清楚地了解价值观和决策的传播;
  • 有注释解释代码为什么这样做,而不是做什么,以避免将来出现偏差;
  • 有独立的文档;
  • 有 有用的错误和有用的失败测试;
  • 可能经常与"聪明"的代码互相排斥。

参考指南:

1. gofmt 统一代码格式

执行 go fmt ./... 来统一代码格式。

2. 注释说明增强

在会引发歧义或者和上文判断不同时使用注释以提醒开发者。

golang 复制代码
if err := doSomething(); err != nil {
    // ...
}

if err := doSomething(); err == nil { // if NO error
    // ...
}
3. 注释应该说明的是"为什么",而不是"做什么"
go 复制代码
// ❌ Bad
// Increment i by 1
i = i + 1

// ✅ Good
// Retry count is increased after each failed attempt.
i++
4. 当有 context.Context 参数时,其永远是方法的第一个参数并且不被放在结构体中
golang 复制代码
// ❌ Bad
type ServerContext struct {
  ctx context.Context
  // other 
}

func (s *ServerContext) doSomething(param any, ctx context.Context) {
  // ...
}

// ✅ Good
type ServerContext struct {
  // other 
}

func (s *ServerContext) doSomething(ctx context.Context, param any) {
  // ...
}
5. 正确声明变量
golang 复制代码
// ❌ Bad
// nil 
s := []int8{}

// ✅ Good
// 非零但长度为零
var s []int

i := 42

\[\]T{} 会默认分配内存,var s \[\]T 更清晰与高效。

在使用非零值初始化时,使用 := 赋值。

6. 代码中不要出现 panic
golang 复制代码
// ❌ Bad
func ParseConfig(file *os.File, cfg any) {
    decoder := yaml.NewDecoder(file)
    if err := decoder.Decode(cfg); err != nil {
        panic("failed to decode config: " + err.Error())
    }
}

// ✅ Good
func Parse(file *os.File, cfg any) error {
    decoder := yaml.NewDecoder(file)
    if err := decoder.Decode(cfg); err != nil {
        return err
    }
    return nil
}

依赖库应该优先返回 err,而不是终止程序。

7. 保持错误信息干净,使用结构化日志
golang 复制代码
// ❌ Bad
return fmt.Errorf("some error.")

// ✅ Good
return fmt.Errorf("some error.")

error msg 小写开头且不以任何标点结尾。

8. 避免多余的 else 逻辑
golang 复制代码
// ❌ Bad
if err != nil {
    if something {
        return err
    }
} else {
    doSomething()
}

// ✅ Good
if err != nil {
    return err
}
doSomething()

尽早返回 error 且避免多余的 else 语句。

9. 使用包别名导入

除非为了避免名称冲突,否则应避免重命名导入;好的包名称不应该需要重命名。如果发生冲突,优先重命名本地或项目特定的导入。

import pkg 按组组织,每组之间以空行分隔。标准库包始终位于第一组,其次是第三方包,最后是项目内部包。

10. 缩写保持大写和官方库一致
golang 复制代码
// ❌ Bad
type HttpServer struct{}

// ✅ Good
type HTTPServer struct{}

标准库写法:HTTP、ID、JSON。保持一致性.

11. 避免裸返回
golang 复制代码
// ❌ Bad
func sum(a, b int) (result int) {
    result = a + b
    return
}

// ✅ Good
func sum(a, b int) int {
    return a + b
}

除非函数非常短,一眼看到底时使用裸返回。

12. 包名、接受者名保持简单与简洁切避免函数名重复与啰嗦
golang 复制代码
// ❌ Bad
package my_utils

func (this *Server) Start() {}

package yamlconfig
func ParseYAMLConfig(input string) (*Config, error)

func OverrideFirstWithSecond(dest, source *Config) error

// 不应书写函数的返回参数名,这会导致 goDoc 混乱
func doSomething(a, b int) (sum int, err error) {
  // logic
}

// ✅ Good
package utils

func (s *Server) Start() 

package yamlconfig
func Parse(input string) (*Config, error)

func Override(dest, source *Config) error

func doSomething(a, b int) (int,error) {
  // logic
}
  • 包名小写、简洁、无下划线;
  • 接受者变量 s, r, c 这种短变量常见且清晰;
  • 包名提供上下文。

当需要消除类似名称的函数歧义时,可以包含额外的信息。

golang 复制代码
// ✅ Good
func (c *Config) WriteTextTo(w io.Writer) (int64, error)
func (c *Config) WriteBinaryTo(w io.Writer) (int64, error)
13. 函数名中不应出现返回类型且命名体现语义
golang 复制代码
// ❌ Bad
func TransformToJSON(input *Config) *jsonconfig.Config

func (c *Config) GetJobName(key string) (value string, ok bool)

// ✅ Good
func Transform(input *Config) *jsonconfig.Config

// 返回值函数中不要动词
func (c *Config) JobName(key string) (value string, ok bool)

// 动作用动词,取值用名词
func (c *Config) ApplyChanges() error
func ProcessData(data []byte) error

动作用动词,取值用名词且返回值函数中不要动词。

14. 测试写法符合 Go got %v, want %v风格
golang 复制代码
// ❌ Bad
if got != want {
    t.Errorf("expected %v but got %v", want, got)
}

// ✅ Good
if got != want {
    t.Errorf("got %v, want %v", got, want)
}
15. 常量与结构化错误声明
golang 复制代码
type Animal string

var (
    // ErrDuplicate occurs if this animal has already been seen.
    ErrDuplicate = errors.New("duplicate")

    // ErrMarsupial occurs because we're allergic to marsupials outside Australia.
    // Sorry.
    ErrMarsupial = errors.New("marsupials are not supported")
)

func process(animal Animal) error {
    switch {
    case seen[animal]:
        return ErrDuplicate
    case marsupial(animal):
        return ErrMarsupial
    }
    seen[animal] = true
    // ...
    return nil
}

在使用到常量定义或者错误时,将其机构化定义在文件顶部或统一管理。

16. 当函数参数列表过多时,使用可变参数处理输入
golang 复制代码
type SshProtocol struct {
	Host                 string
	Port                 string
	Timeout              string
	Username             string
	Password             string
	PrivateKey           string
	PrivateKeyPassphrase string
	ReuseConnection      string
	Script               string
	ParseType            string
	ProxyHost            string
	ProxyPort            string
	ProxyUsername        string
	ProxyPassword        string
	UseProxy             string
	ProxyPrivateKey      string
}

type SshProtocolConfigOptFunc func(option *SshProtocol)

func NewSshProtocol(host, port string, opts ...SshProtocolConfigOptFunc) *SshProtocol {

	option := &SshProtocol{
		Host: host,
		Port: port,
	}

	for _, opt := range opts {
		opt(option)
	}

	return &SshProtocol{
		Host:                 host,
		Port:                 port,
		Timeout:              option.Timeout,
		Username:             option.Username,
		Password:             option.Password,
		PrivateKey:           option.PrivateKey,
		PrivateKeyPassphrase: option.PrivateKeyPassphrase,
		ReuseConnection:      option.ReuseConnection,
		Script:               option.Script,
		ParseType:            option.ParseType,
		ProxyHost:            option.ProxyHost,
		ProxyPort:            option.ProxyPort,
		ProxyUsername:        option.ProxyUsername,
		ProxyPassword:        option.ProxyPassword,
		UseProxy:             option.UseProxy,
		ProxyPrivateKey:      option.ProxyPrivateKey,
	}
}

func (sp *SshProtocol) IsInvalid() error {

	return nil
}
17. 字符串拼接
golang 复制代码
// 连接少量字符时使用 + 
key := "projectid: " + p

// 构建带有格式化的复杂字符串时,优先使用 fmt.Sprintf
str := fmt.Sprintf("%s [%s:%d]-> %s", src, qos, mtu, dst)

// 字符串格式更加复杂时,优先使用 text/template 或 safehtml/template
相关推荐
Csvn几秒前
Nginx 配置与运维管理 — 从安装到 SSL 反向代理
后端
mqcode1 小时前
若依框架做大了怎么办?多模块 Maven 拆分的完整指南
后端
用户40269244819082 小时前
CRMEB Pro 新增后台接口全链路:路由、权限、验证器、返回格式一次讲清
前端·后端
考虑考虑2 小时前
Java实现hmacsha1加密算法
java·后端·java ee
程序边界2 小时前
lac_agent自愈链路上篇——crontab守护的那些坑与健康检查实战
后端
笨鸟飞不快3 小时前
从 MVC 到 DDD:一次真实的渐进式迁移实录
后端·架构
程序员威哥3 小时前
C#也能玩转YOLO:工业视觉原生推理方案,零Python依赖
后端
kfaino3 小时前
你好,我叫 Prompt——其实,你一直在给 AI 写程序
后端·openai·ai编程
caibixyy3 小时前
springboot+langchain4j实战Day 16 — 混合检索 + Reranker 重排序
后端
Ai拆代码的曹操3 小时前
揭秘"幽灵 CPU":top 抓不到的短命进程,才是真正的 CPU 杀手
后端