高质量编程
1.代码格式
这里推荐使用Go语言官方的提供的工具gofmt,此处列出vscode配置gofmt的步骤,如下:
Format On Save将Format On Save选项打勾后重启即设置完成好的注释能让人看到代码的同时眼前一亮,并且简单明了地表达出自己的逻辑思考。注释应从以下四方面入手:
- 解释代码的作用:一些公共符号
- 解释代码如何做的:代码的实现过程
- 解释代码实现的原因:添加段代码的上下文因素
- 解释代码什么情况会出错:解释代码的限制条件
- 越简洁越好
- 缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写
- 变量在上下文中使用范围越广,变量名所应包含的信息就越多,最典型的就是全局变量,不仅变量名要有明确的含义,更应在第一次声明时加以注释
- 避免嵌套,保证正常流程清晰
- 保持正常代码路径为最小缩进,即优先考虑错误情况或特殊情况,其次才是正常流程代码。这样后续需要添加特殊/错误情况,只需要额外再加一次判断,而不用再嵌套,代码看上去也更易读
- 简单错误,即只出现一次且其他地方不再需要的捕获该错误,则使用errors.New来创建匿名变量表明该错误;如果需要格式化的需求,则使用fmt.Errorf
- 错误的Warp和Unwarp
- 错误的Wrap是指在原始错误上添加额外的上下文信息,并返回一个新的错误。这样可以保留错误的根本原因,同时提供更多的上下文信息,方便调试和追踪错误。常见的错误的Wrap函数是
errors.Wrap,它接收一个错误和一个字符串作为参数,返回一个新的错误,新的错误包含了原始错误和额外的上下文信息。 - 错误的Unwrap是指从错误链中获取原始错误。当我们需要查看错误链中的每个错误时,可以使用错误的Unwrap函数。常见的错误的Unwrap函数是
errors.Unwrap,它接收一个错误作为参数,返回该错误链中的下一个错误。
err := errors.New("原始错误")
newErr := errors.Wrap(err, "额外的上下文信息")//添加额外错误信息
unwrappedErr := errors.Unwrap(newErr)//获取原始错误信息
- 错误判定:用于判断错误是否是某个特定类型,并将错误转换为该类型;注意,在使用
errors.As函数之前,需要保证目标类型实现了error接口,即具有Error() string方法。使用errors.As函数时,它会尝试将err转换为target指向的类型。如果成功转换,返回true,否则返回false。如果返回true,可以通过类型断言来访问转换后的错误,如果返回false,说明错误无法转换为目标类型
type MyError struct {
msg string
}
//定义自己的错误类型,并为错误提供自定义的描述信息
func (e *MyError) Error() string {
return e.msg
}
func main() {
err := MyError{"自定义错误"}
var target *MyError
if errors.As(err, target) {
fmt.Println("错误转换成功")
fmt.Println(target)
} else {
fmt.Println("错误无法转换为目标类型")
}//err是一个MyError类型的实例,target是一个指向MyError类型的指针
//由于err的类型和target指向的类型一致,因此转换是成功的
//所以,errors.As函数会返回true,表示错误转换成功,程序会打印出"错误转换成功"
}
panic来中断程序的正常执行流程,并触发一个运行时的异常。当程序执行到panic语句时,立即停止当前函数的执行,并向上返回堆栈,直到被调用的函数中使用了recover函数进行处理,或者程序终止。如果在当前函数中没有使用recover函数来处理panic,那么程序会终止并打印出调用栈信息和panic的值func main() {
fmt.Println("Start")
doSomething()
fmt.Println("End")
}
func doSomething() {
fmt.Println("Doing something")
//执行到这里时会立即停止当前函数的执行,并触发一个运行时异常。
panic("Something went wrong")
}
//由于没有使用recover函数来处理panic,所以程序会终止并打印出调用栈信息和panic的值
//即输出
// Start
// Doing something
// panic: Something went wrong
//再加上对应栈信息
defer语句中捕获并处理panic,防止程序因为panic而终止。当在defer函数中调用recover时,它会返回panic的值(即panic函数中传递的参数),如果没有发生panic,则recover函数会返回nil。需要注意的是,recover必须在defer函数中调用才能生效。同时,recover只能捕获当前协程中的panic,无法捕获其他协程的panicfunc main() {
defer func() {
//捕获并返回panic的值
if err := recover(); err != nil {
fmt.Println("发生了panic:", err)
// 进行相应的错误处理
}
}()
fmt.Println("Start")
doSomething()
fmt.Println("End")
}
func doSomething() {
fmt.Println("Doing something")
//触发一个运行时异常
panic("Something went wrong")
}