我们为了保证整个项目的一致性、可读性,对注释规范做了更加严格和详细的修改。
PR评审时务必注意代码注释问题。
(1)注释必须只能使用英文进行注释,对于字符之间应当严格使用空格分隔。
(2)除了包注释外注释使用 /**/ 多行注释外,其他注释风格应当使用单行注释 // 。
样例:
Bad:
/**
...
**/
func listUser() error{
...
}
Good:
// listUser lists all users of department
func listUser() error{
...
}
(3)单行注释不要过长,应保持和代码风格一致,不应超过120个字符;超过的请使用换行展示,尽量保持格式优雅,便于阅读
(4)单行注释中的注释标记符和注释内容应以单个空格隔开
样例:
Good:
// listUser lists all users of department
func listUser() error{
...
}
Bad:
//listUser lists all users of department
func listUser() error{
...
}
(5)所有注释都应当使用第三人称单数。
样例:
Bad:
// listUser list all users of department
func listUser() error{
...
}
(6)在代码中,请避免每行代码都注释,应控制注释的数量,提高注释的质量
注释风格严格遵循以上要求。
/*
* Author: xiexiaoyu
*
* START DATE: 2023/2/25 14:28
*
* CONTACT: [email protected]
*/
文件注释应当通过代码注释模板自动注释,在Goland的注释模板应当为:
/*
* Author: xiexiaoyu
*
* START DATE: ${DATE} ${TIME}
*
* CONTACT: [email protected]
*/
package ${GO_PACKAGE_NAME}
(1)每个自定义的结构体或者接口都应该有注释说明,该注释对结构进行简要介绍,放在结构体定义的前一行,格式为: 结构体名, 结构体说明
(2)如果结构体属性较多,则结构体内的每个成员变量添加注释
// User defines user info
type User struct {
// user name
Username string
// email
Email string
}
在一些结构体中,一些简单的约定俗成的字段可不进行注释,比如:
Name
Email
......
但应当注意,当一个结构体出现一些易于混淆的字段时,即使字段再简单也应当对其进行注释用以区分。
样例:
Good:
type PermissionModel struct {
UGID string
Path string
Method string
// english name
Nickname string
Pid string
}
Bad:
像下面这个示例,所有字段都没注释,而且很多字段都是该模块中专有的名词,阅读代码的人一脸懵逼
type yciRepo struct {
db *Data
cron *gron.Cron
vmCache *go_cache.Cache
sc string
snapshotClass string
mlockImg string
consul string
remoteProm string
vmWhiteLIst []string
log *log.Helper
operator map[string]func(yciID string) error
}
(3)如果为接口,则以以下方式描述:
// FileInfo is the interface that describes a file and is returned by Stat and Lstat.
type FileInfo interface {}
样例:
Good
Bad:
没注释
type UserRepo interface {
SSOLogin(ctx context.Context, ticket string) (*Token, error)
GetWxUserInfo(ctx context.Context, uid string) (*WxUserInfo, error)
GetUserInfo(ctx context.Context, uid string) (*Member, error)
ListRandomUsers(ctx context.Context, num int32) ([]*MemberMeta, error)
ListRecommendUsers(ctx context.Context, num int32) ([]*MemberMeta, error)
UpdateUserTags(ctx context.Context, uid string, tags []string) error
UpdateUserAvatarUrl(ctx context.Context, uid, avatarUrl string) error
}
(1)每个函数,或者方法(结构体或者接口下的函数称为方法)都应该有注释说明
(2)函数注释不区分大写函数还是小写函数,特别是一些比较复杂的函数,必须注释
样例:
Bad:
func (u *userRepo) syncUsers() {
...
}
函数注释不区分大小写函数,应该对函数做一个详细的描述,特别简单的除外。
Good:
// syncUsers ...
// step1: ...
// step2: ...
// step3: ...
func (u *userRepo) syncUsers() {
...
}
(3)注释的首个单词应该和函数名保持一致
// Dial connects to the address on the named network.
func Dial(network, address string) (Conn, error) {
...
}
(4)如果在函数或者方法注释中例子能更好地阐明函数使用以及意图,请添加适当的例子
// Dial connects to the address on the named network.
//
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
// (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and
// "unixpacket".
//
// Examples:
// Dial("tcp", "golang.org:http")
// Dial("tcp", "192.0.2.1:http")
// Dial("tcp", "198.51.100.1:80")
// Dial("udp", "[2001:db8::1]:domain")
// Dial("udp", "[fe80::1%lo0]:53")
// Dial("tcp", ":80")
func Dial(network, address string) (Conn, error) {
}
// 代码块的执行逻辑
if result > 0 {
}
if err != nil {
}
const (
// ID is the identifier of container
ID = "id"
)
(1)当某个部分等待完成时,可用 TODO(name): 开头的注释来提醒开发人员。
func GetComment(ctx context.Context, objId string) ([]*biz.CommentMeta, error) {
// TODO(xiexiaoyu): add limit and offset
...
}
(2)当某个部分存在已知问题进行需要修复或改进时,可用 FIXME: 开头的注释来提醒开发人员。
func DeleteComment(ctx context.Context, uid, objId string, id int) error {
// FIXME(xiexiaoyu): delete comment
...
}
(3)当需要特别说明某个问题时,可用 NOTE: 开头的注释:
// NOTE: os.Chmod and os.Chtimes don't recognize symbolic link,
// which will lead "no such file or directory" error.
return os.Symlink(target, dest)