完整教程:Go语言编码规范

news/2026/1/20 10:24:02/文章来源:https://www.cnblogs.com/yangykaifa/p/19505193

一、 版本记录
二、 编码风格规范
2.1、 格式化
2.1.1、 缩进
2.1.2、 行长度
2.1.3、 括号
2.2、 命名规则
2.2.1、 包名
2.2.2、 函数名、方法名
2.2.3、 结构体名
2.2.4、 接口名
2.2.5、 变量名
2.2.6、 常量名
2.3、 变量、常量定义
2.4、 变量类型定义
2.4.1、 String 类型定义
2.4.2、 Slice、Map 类型定义
2.4.3、 结构体定义
2.5、 接口定义
2.6、 函数、方法定义
2.7、 错误处理
2.8、 其他说明
三、 注释规范
3.1、 注释风格
3.2、 包注释
3.3、 函数、方法注释
3.4、 结构体、接口注释
3.5、 其他说明


一、 版本记录

日期 版本  修改描述修订者审核者
2025-12-19v0.1初始版本研发部研发部

二、 编码风格规范

2.1、 格式化


1、 go 中主要通过 go fmt 程序(也可以用 go fmt)将 go 程序按照标准风格缩进、对齐、保留注释并在需要时重新格式化。

2、 使用vscode,安装 go 拓展及 go tools 之后,即可自动格式化成标准风格。

2.1.1、 缩进


1、 go 中使用 tab 缩进,go fmt 也是默认使用 tab 。建议将 tab 设置为4个空格。

2.1.2、 行长度


1、 go 对行的长度没有限制。但是我们还是建议行的长度不要超过120,应该在达到这个限制之前换行。

2.1.3、 括号


1、 go 中的控制结构(if、for、switch)在语法上不需要圆括号。

2.2、 命名规则


2.2.1、 包名


1、 全部小写。没有大写或下划线,不建议用驼峰。

2、 大多数使用命名导入的情况下,不需要重名。

3、 简短而简洁,(如果你对缩写熟悉的话,可以在保证见名知意的前提下使用缩写)。

4、 不要用复数。

5、 不要用“common”、“util”、“lib”。这些不好,信息量不足。

2.2.2、 函数名、方法名


1、 遵循 go 社区对函数名的约定,使用驼峰风格(例如:MixedCaps 或者 mixedCaps )。

2、 规则:动词 + 名词。

3、 如果函数、方法名是判断类型(返回值主要为bool类型),则名称应以 Has、Is、Can 等判断性动词开头。

4、 注意:开头大写和小写,在 go 中的区别。

2.2.3、 结构体名


1、 结构体名应该是驼峰风格的名词或者名词短语。(例如:Custome、WikiPage、Account)

2.2.4、 接口名


2、 以“er”作为后缀,例如 Reader、 Writer、 Formatter、CloseNotifier 等。接口实现的方法则去掉“er”,例如:Read、Write 等。

2.2.5、 变量名


1、 变量名遵循驼峰风格。

2、 首字母根据访问控制原则使用大写或小写。

3、 对于旨在本文件中有效的顶级变量,应该使用“_”前缀,避免在同一个包中的其他文件中意外使用错误的值。

4、 如果变量为 bool 类型,则名称应以 Has、Is、Can等开头。

2.2.6、 常量名


1、 常量全部大写,使用“_”分词。

2、 对于旨在本文件中有效的顶级常量,应该使用“_”前缀,避免在同一个包中的其他文件中意外使用错误的值。

3、 如果常量为 bool 类型,则名称应以 HAS、IS、CAN等开头。

2.3、 变量、常量定义


1、 常量只能是数字、字符、字符串或布尔值。枚举常量使用枚举器 iota 创建。

2、 枚举从1开始。除非0有意义

3、 因为变量的默认值为0, 因此通常以非零值开头枚举,以区分默认值。

4、 函数内使用短变量声明(:=);函数外使用长变量声明(var)。var 关键字一般用于包级别变量声明,或者函数内的零值情况。

5、 变量、常量的分组声明一般需要按照功能来区分,而不是将所有类型都分在一组,枚举常量,需要先创建相应类型:

type ParseError int
const (ERR_SECTION_NOT_FOUND ParseError = iota + 1ERR_KEY_NOT_FOUNDERR_BLANK_SECTION_NAMEERR_COULD_NOT_PARSE
)

6、 通常情况下,尽量缩小变量的作用范围。

反例:
err := ioutil.WriteFile(name, data, 0644)
if err != nil {return err
}
正例:
if err := ioutil.WriteFile(name, data, 0644); err != nil {return err
}

7、 如果需要在 if 之外使用函数调用的结果,则不应尝试缩小变量的作用范围。

data, err := ioutil.ReadFile(name)
if err != nil {return err
}
if err := cfg.Decode(data); err != nil {return err
}
fmt.Println(cfg)
return nil

2.4、 变量类型定义


2.4.1、 String 类型定义


1、 声明 Printf-style String 时,将其设置为 const 常量,有助于 go vet 对 String 类型实例执行静态分析。

const msg = "unexpected values %v, %v\n"
fmt.Printf(msg, 1, 2)

2、 优先使用 strconv 而不是 fmt, 将原语转换为字符串或从字符串转换时, strconv 速度比 fmt 快。

3、 避免字符串到字节的转换,不要反复从固定字符串创建字节 Slice,执行一次性完成转换。

反例:
for i := 0; i < b.N; i++ {w.Write([]byte("Hello world"))
}
正例:
data := []byte("Hello world")
for i := 0; i < b.N; i++ {w.Write(data)
}

2.4.2、 Slice、Map 类型定义


1、 尽可能指定容器的容量,以便为容器预先分配内存,向 make() 传入容量参数会在初始化时尝试调整 Slice、Map 类型实例的大小,这将减少在将元素添加到 Slice、Map 类型实例时的重新分配内存造成的损耗。

2、 使用 make() 初始化 Map 类型变量,使得开发者可以很好的区分开 Map 类型实例的声明,或初始化。使用 make() 还可以方便地添加大小提示。

3、 如果 Map 类型实例包含固定的元素列表,则使用 map literals(map 初始化列表)的方式进行初始化:

反例:
m := make(map[T1]T2, 3)
m[k1] = v1
m[k2] = v2
m[k3] = v3
正例:
m := map[T1]T2{k1: v1,k2: v2,k3: v3,
}

4、 在追加 Slice 类型变量时优先指定切片容量,在初始化要追加的切片时为 make() 提供一个容量值。

for n := 0; n < b.N; n++ {data := make([]int, 0, size)for k := 0; k < size; k++{data = append(data, k)}
}

5、 Map 或 Slice 类型实例是引用类型,所以在函数调用传递时,要注意在函数内外保证实例数据的安全性,除非你知道自己在做什么。这是一个深拷贝和浅拷贝的问题。

反例:
func (d *Driver) SetTrips(trips []Trip) {d.trips = trips
}
trips := ...
d1.SetTrips(trips)
// 你是要修改 d1.trips 吗?
trips[0] = ...
正例:
func (d *Driver) SetTrips(trips []Trip) {d.trips = make([]Trip, len(trips))copy(d.trips, trips)
}
trips := ...
d1.SetTrips(trips)
// 这里我们修改 trips[0],但不会影响到 d1.trips。
trips[0] = ...

6、 返回 Map 或 Slice 类型实例时,同样要注意用户对暴露了内部状态的实例的数值进行修改。

反例:
type Stats struct {mu sync.Mutex counters map[string]int
}
// Snapshot 返回当前状态。
func (s *Stats) Snapshot() map[string]int {s.mu.Lock()defer s.mu.Unlock() return s.counters
}
// snapshot 不再受互斥锁保护。
// 因此对 snapshot 的任何访问都将受到数据竞争的影响。
// 影响 stats.counters。·
snapshot := stats.Snapshot()
正例:
type Stats struct {mu sync.Mutex counters map[string]int
}
func (s *Stats) Snapshot() map[string]int {s.mu.Lock()defer s.mu.Unlock() result := make(map[string]int, len(s.counters))for k, v := range s.counters { result[k] = v}return result
}
// snapshot 现在是一个拷贝
snapshot := stats.Snapshot()

2.4.3、 结构体定义


1、 通常情况下,初始化结构体应该始终指定字段名。

k := User{FirstName: "John",LastName: "Doe",Admin: true,
}

2、 但是初始化具有字段名的结构体时,除非提供有意义的上下文,否则忽略值为零的字段。 也就是说让我们自动将这些设置为零值 。( 有助于通过省略该上下文中的默认值来减少阅读的障碍 )

反例:
user := User{FirstName: "John",LastName: "Doe",MiddleName: "",Admin: false,
}
正例:
user := User{FirstName: "John",LastName: "Doe",
}

3、 如果在声明中省略了结构的所有字段,请使用 var 声明结构。

反例:
user := User{}
正例:
var user User

4、 嵌入结构体中作为成员的结构体,应位于结构体内的成员列表的顶部,并且必须有一个空行将嵌入式成员与常规成员分隔开。

5、 在初始化 Struct 类型的指针实例时,使用 &T{} 代替 new(T),使其与初始化 Struct 类型实例一致。

sval := T{Name: "foo"}
sptr := &T{Name: "bar"}

2.5、 接口定义


1、 特别的,如果希望通过接口的方法修改接口实例的实际数据,则必须传递接口实例的指针(将实例指针赋值给接口变量),因为指针指向真正的内存数据。

type F interface {f()
}
type S1 struct{}
func (s S1) f() {}
type S2 struct{}
func (s *S2) f() {}
// f1.f() 无法修改底层数据。
// f2.f() 可以修改底层数据,给接口变量 f2 赋值时使用的是实例指针。
var f1 F := S1{}
var f2 F := &S2{}

2.6、 函数、方法定义


1、 函数、方法的参数排列顺序遵循以下几点原则(从左到右)。

参数的重要程度与逻辑顺序。
简单类型优先于复杂类型。
尽可能将同种类型的参数放在相邻位置,则只需写一次类型。


2.7、 错误处理


1、 err 总是作为函数返回值列表的最后一个。

2、 如果一个函数 return err,一定要检查它是否为空,判断函数调用是否成功。如果不为空,说明发生了错误,一定要处理它。

3、 不能使用 _ 丢弃任何 return 的 err。若不进行错误处理,要么再次向上游 return err,要么使用 log 记录下来。

4、 尽早 return err,函数中优先进行 return 检测,遇见错误则马上 return err。

5、 错误提示(Error Strings)不需要大写字母开头的单词,即使是句子的首字母也不需要。除非那是个专有名词或者缩写。同时,错误提示也不需要以句号结尾,因为通常在打印完错误提示后还需要跟随别的提示信息。

6、 尽量不要使用 panic,除非你知道你在做什么。只有当实在不可运行的情况下采用 panic,例如:文件无法打开,数据库无法连接导致程序无法正常运行。但是对于可导出的接口不能有 panic,不要抛出 panic 只能在包内采用。建议使用 log.Fatal 来记录错误,这样就可以由 log 来结束程序。

7、 如果我们需要用函数的返回值来初始化某个变量,应该把这个函数调用单独写在一行,例如:

反例:
if x, err := f(); err != nil { // error handling return
} else { // use x
}
正例:
x, err := f()
if err != nil { // error handling return
}

8、 采用独立的错误流进行处理。尽可能减少正常逻辑代码的缩进,这有利于提高代码的可读性,便于快速分辨出哪些还是正常逻辑代码,例如:

反例:
if err != nil { // error handling
} else { // normal code
}
正例:
if err != nil { // error handling return // or continue, etc.
}

2.8、 其他说明


1、 减少嵌套,代码应通过尽可能先处理错误情况/特殊情况并尽早返回或继续循环来减少嵌套。减少嵌套多个级别的代码的代码量。

反例:
for _, v := range data {if v.F1 == 1 {v = process(v)if err := v.Call(); err == nil {v.Send()} else {return err}} else {log.Printf("Invalid v: %v", v)}
}
正例:
for _, v := range data {if v.F1 != 1 {log.Printf("Invalid v: %v", v)continue}v = process(v)if err := v.Call(); err != nil {return err}v.Send()
}

2、 减少不必要的else。

反例:
var a int
if b {a = 100
} else {a = 10
}
正例:
a := 10
if b {a = 100
}

3、 顶层变量,使用标准的 var 关键字。请勿指定类型,除非它与表达式的类型不同。

反例:
var _s string = F()
func F() string { return "A" }
正例:
var _s = F()
// 由于 F 已经明确了返回一个字符串类型,因此我们没有必要显式指定_s 的类型
// 还是那种类型
func F() string { return "A" }

4、 使用 defer 释放资源,比如文件和锁。但是需要注意,无限循环中禁止使用defer。除非在匿名函数中。

5、 Channel 的 size 要么是 1,要么是无缓冲的

三、 注释规范


3.1、 注释风格


1、 统一使用中文注释,中英文之间使用空格分割,严格使用中文标点符号。

2、 注释应当是一个完整的句子,以句号结尾。

3、 句子类型的注释首字母均需大写,短语类型的注释首字母需小写。

4、 注释的单行长度不能超过80个字符。

3.2、 包注释


1、 每个包都应该有一个包注释。放置在包前的一个块注释。对于包含多个文件的包,包注释只需出现在其中的一个文件中即可。包注释应该包含:

包名、简介。
创建者。
创建时间。
2、 对于main包,通常只有一行简短的注释用以说明包的用途,且以项目名称开头。

3、 对于简单的非main包,也可用一行注释概括。

4、 对于一个复杂项目的子包,一般情况下不需要包级别注释,除非是代表某个特定功能的模块。

5、 对于相对功能复杂的非main包,一般都会增加一些使用示例或基本说明,且以Package开头。

6、 对于特别复杂的包说明,一般使用doc.go文件用于编写包的描述,并提供与整个包相关的信息。

3.3、 函数、方法注释


1、 每个函数、方法(结构体或者接口下属的函数称为方法)都应该有注释说明,包括三个方面(顺序严格):

函数、方法名,简要说明。

参数列表,每行一个参数。

返回值,每行一个返回值。

2、 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述。

3、 若函数或方法为判断类型(返回值主要为bool类型),则注释以:

 returns true if开头
// HasPrefix returns true if name has any string in given slice as prefix.
func HasPrefix(name string, prefixes []string) bool {...}

3.4、 结构体、接口注释


每个自定义的结构体、接口都应该有注释说明,放在实体定义的前一行,格式为:名称、说明。同时,结构体内的每个成员都要有说明,该说明放在成员变量的后面(注意对齐),例如:

// User, 用户实例,定义了用户的基础信息。
type User struct {Username string // 用户名Email string // 邮箱
}

3.5、 其他说明


1、 当某个部分等待完成时,用 TODO(Your name): 开头的注释来提醒维护人员。

2、 当某个部分存在已知问题进行需要修复或改进时,用 FIXME(Your name): 开头的注释来提醒维护人员。

3、 当需要特别说明某个问题时,可用 NOTE(You name): 开头的注释。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1188439.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自我介绍+软工5问

这个作业属于哪个课程 班级的链接这个作业要求在哪里 作业要求的链接---- ----这个作业的目标 自我介绍、阅读并理解教材、总结自己想要获得的收获,同时熟悉博客网及Github和Git操作自我介绍 您好,我叫黄思聪,目前是…

当教育数据“活”起来:书匠策AI用可视化魔法重塑科研叙事——解锁学术图表的“未来语言”

在学术写作的江湖里&#xff0c;“一图胜千言”早已不是秘密。但当教育研究者面对堆积如山的数据时&#xff0c;如何让柱状图跳出“千篇一律”的框架&#xff1f;如何让动态趋势图在期刊编辑眼前“一眼惊艳”&#xff1f;如何让跨学科图表同时满足《教育研究》的严谨与《Nature…

2026年伞齿轮设计厂家推荐指南:伞齿轮/内齿圈/ 内齿圈/直伞齿轮/工业伞齿轮 - 品牌策略师

2026年伞齿轮设计厂家推荐指南:伞齿轮/内齿圈/ 内齿圈/直伞齿轮/工业伞齿轮在高端装备制造领域,伞齿轮作为关键传动部件,其设计精度与制造水平直接决定了机械设备的性能与寿命。随着工业4.0与智能制造浪潮的推进,市…

自动售货机系统设计(有完整资料)

资料查找方式&#xff1a;特纳斯电子&#xff08;电子校园网&#xff09;&#xff1a;搜索下面编号即可编号&#xff1a;CJ-51-2021-004设计简介&#xff1a;本设计是自动售货机系统设计&#xff0c;主要实现以下功能&#xff1a;可实现LCD12864显示商品的名称数量以及价格&…

学术图表进化论:书匠策AI如何用“视觉语法”重构科研叙事

——当数据可视化成为学术话语的“第二语言” 在学术圈&#xff0c;一张优秀的图表往往比千言万语更具说服力。然而&#xff0c;传统科研绘图工具的局限性正让研究者陷入“数据富矿、表达贫瘠”的困境&#xff1a;手动调整格式耗时耗力、跨学科图表规范难以兼顾、静态图表难以…

当教育科研遇上“可视化魔法”:书匠策AI科研绘图功能全解析

在学术江湖里&#xff0c;论文写作是一场“创意与严谨”的双重博弈。有人为数据可视化愁到脱发——想用动态地图展示教育政策影响&#xff0c;却卡在Excel数据清洗&#xff1b;想用三维关系图解析教育技术生态&#xff0c;却因工具模板有限只能画柱状图&#xff1b;更别提不同期…

2026年实验室设备选型指南:从硬件采购到智能化、合规与成本最优的战略选择 - 2026年企业推荐榜

引言:企业核心痛点与选型总览 在2025-2026年的产业周期中,中国实验室运营者正面临一个根本性的范式转换:实验室设备已不再是孤立的“仪器”,而是一个需要融入数字化工作流、满足日益严苛的合规要求、并优化全生命周…

互联网大厂Java求职面试实战:微服务与AI技术全解析

互联网大厂Java求职面试实战&#xff1a;微服务与AI技术全解析 本文通过模拟互联网大厂Java求职者谢飞机的面试过程&#xff0c;聚焦微服务、数据库、缓存、安全及AI技术&#xff0c;结合电商场景&#xff0c;逐步深入考察技术细节&#xff0c;帮助求职者系统理解核心技术与业务…

当教育论文遇上“可视化魔法”:书匠策AI科研绘图功能全解析

在学术江湖中&#xff0c;论文绘图常被视为“技术流”的终极考验&#xff1a;有人为Excel的折线图配色纠结三天&#xff0c;有人因SPSS数据格式不兼容被迫重做实验&#xff0c;更有人因期刊对图例位置的苛刻要求被拒稿五次……直到一款名为书匠策AI的科研工具横空出世&#xff…

2025年行业内口碑好的现浇搭建报价,现浇楼板/现浇钢筋混凝土/现浇阁楼/楼板搭建/钢筋混凝土现浇,现浇搭建施工口碑推荐 - 品牌推荐师

近年来,随着城市住宅结构升级与个性化空间改造需求的增长,现浇搭建行业迎来快速发展期。从别墅地下室扩建到LOFT夹层改造,从混凝土楼板浇筑到旋转楼梯定制,消费者对施工质量、材料透明度及服务专业性的要求持续提升…

性能测试新纪元:AI模拟真实用户行为

从脚本到智能的范式转移 在软件测试领域&#xff0c;性能测试一直是确保系统稳定性、可扩展性和用户体验的核心环节。传统的性能测试方法&#xff0c;如基于脚本的工具&#xff08;如JMeter或LoadRunner&#xff09;&#xff0c;通过预设的用户行为模式模拟负载&#xff0c;但…

测试覆盖率提升:AI算法优化实战解析

测试覆盖率的挑战与AI破局契机 在敏捷开发和持续交付环境中&#xff0c;测试团队面临核心矛盾&#xff1a;快速迭代需求与深度测试覆盖难以兼顾。传统覆盖率统计方法虽量化测试范围&#xff0c;但常遗漏关键路径盲区&#xff0c;且无法动态优化策略。AI技术通过智能缺口识别、…

基于GSM的家庭安防系统设计(有完整资料)

资料查找方式&#xff1a;特纳斯电子&#xff08;电子校园网&#xff09;&#xff1a;搜索下面编号即可编号&#xff1a;CJ-51-2021-005设计简介&#xff1a;本设计是基于GSM短信模块的家庭安防报警系统&#xff0c;主要实现以下功能&#xff1a;1、使用温度传感器检测室内温度…

基于单片机的LED照明系统的设计(有完整资料)

资料查找方式&#xff1a;特纳斯电子&#xff08;电子校园网&#xff09;&#xff1a;搜索下面编号即可编号&#xff1a;CJ-51-2021-006设计简介&#xff1a;本设计是基于单片机的LED照明系统的设计&#xff0c;主要实现以下功能&#xff1a;可实现LCD1602显示光照强度&#xf…

四维云开放平台上线狂欢月:您的时空智能“新基建”,来了!

遥感数据不再难找、处理不再麻烦&#xff0c;一切变得像点外卖一样简单&#xff0c;因为一个真正开放、智能的时空信息平台正式登场。 您是否还在为遥感数据获取困难、处理复杂而犯愁&#xff1f;您是否曾因技术门槛过高&#xff0c;无法将地理信息能力快速融入业务&#xff1f…

基于单片机的PM2.5检测系统(有完整资料)

资料查找方式&#xff1a;特纳斯电子&#xff08;电子校园网&#xff09;&#xff1a;搜索下面编号即可编号&#xff1a;CJ-51-2021-007设计简介&#xff1a;本设计是基于单片机的PM2.5检测系统&#xff0c;主要实现以下功能&#xff1a;可实现LCD1602显示PM2.5的具体数值以及最…

2026年广东优秀的青少年心理辅导中心哪家好,青少年厌学/叛逆孩子教育/青春期教育,青少年心理辅导工作室口碑推荐 - 品牌推荐师

近年来,随着社会竞争压力加剧、家庭教育模式转型及青少年心理健康问题频发,青少年心理辅导行业迎来快速发展期。数据显示,我国12-18岁青少年中,约20%存在不同程度的心理困扰,而家长对专业心理干预的需求年均增长1…

打破传统屏障:交互式芯片3D动画让半导体设备的复杂性触手可及

半导体行业是现代科技的核心&#xff0c;从智能手机到先进的计算机芯片&#xff0c;几乎所有现代电子设备都离不开半导体技术的支持。然而&#xff0c;半导体设备的复杂性往往让非业内人士难以理解&#xff0c;这不仅阻碍了潜在客户的购买决策&#xff0c;也使得技术交流变得困…

解码芯片白皮书:半导体3D视觉图示如何增强技术报告的洞察力

在半导体行业的每一年&#xff0c;技术进步与市场动态变化都会汇聚成一份年度报告或白皮书。这些报告不仅是业内人士的指路明灯&#xff0c;更是投资者、政策制定者以及学术研究者的重要参考。随着技术的飞速发展&#xff0c;传统的文字与二维图形逐渐难以完全准确地表达复杂的…

JavaScript 数组 find 方法详解(附实战示例)

在 JavaScript 开发中&#xff0c;数组查找是高频需求。ES6 新增的 find 方法&#xff0c;凭借“精准查找首个匹配元素”的特性&#xff0c;成为替代传统 for 循环的高效方案。本文从语法、参数、返回值、应用场景、注意事项及实战示例多维度&#xff0c;带你吃透 find 方法的使…