需要切换下状态
一直看各种炫技的语法糖和写法很烧脑...需要配合看框架和项目讲解视频来解压下脑子,不然会有厌烦心理的
另外发现 每日英语听力 的打卡还蛮好使,相当于给定了一个需要完成的目标,每天都能够得到,也比看花时间的掘金长文解压一点
后面还有几个challenge 就先不看了,先看继续后面能简单看完的,剩下的 challenge 明天再去看
23 err-handling
log.Fatalln(err)
panic(err)
log.Fatalln 也会带有退出的效果,如果有 log.Fatalln 的话,后面的 panic 就会执行了,另外退出码还不一样,log.Fatalln 退出码是1,panic 是2
log 设置格式可以通过 log.SetFlags(log.Llongfile) 来实现
如果设置了 log 输入到文件中的话,控制台不会有任何显示,只有一个退出代码
package main
import (
"fmt"
"log"
"os"
)
func init() {
nf, err := os.Create("log.txt")
if err != nil {
fmt.Println(err)
}
log.SetOutput(nf)
log.SetFlags(log.Llongfile)
}
func main() {
_, err := os.Open("no-file.txt")
if err != nil {
// fmt.Println("err happened", err)
log.Println("err happened", err)
// log.Fatalln(err)
// panic(err)
}
log.Println("aaaaa")
log.Fatalln("hello")
}
输出是
03_log-set-output % go run main.go
exit status 1
关于错误处理,有 errors.New 的用法和fmt.Errorf 的用法,见下面的这个例子
func fab(n int) (int, error) {
if n < 0 {
err := errors.New("n must bigger than 0")
return 0, err
}
return n, nil
}
func main() {
fmt.Println(10)
fab(-1)
}
注意几个点:
- 我想使用命名返回值直接返回n 写法是
func fab(n int) (x int, err error) {
x = n
if n < 0 {
err = errors.New("n must bigger than 0")
return
}
return
}
必须要写成这样才行,不能写成
那么还不如不用命名返回值
- err 的类型是error 但是引用的包是errors 多了个s 要记住按
还有一种写法是定义好error的类型
- 另外如果没有错误捕获的话,那么控制台根本不会打印出来error,像是正常输出一样,所以还是要判断一下返回值,如果是error的话就打印出来
另外一种定义error的写法就是
var ErrorIs0 = errors.New("must bigger than 0")
func fab(n int) (int, error) {
if n < 0 {
return -1, ErrorIs0
}
return n, nil
}
func main() {
fmt.Println(10)
_, err := fab(-1)
if err != nil {
log.Fatal(err)
}
}
另外一种写法就是 fmt.Errorf
func fab(n int) (int, error) {
if n < 0 {
return -1, fmt.Errorf("must bigger than 0 but give %d", n)
}
return n, nil
}
func main() {
fmt.Println(10)
_, err := fab(-1)
if err != nil {
log.Fatal(err)
}
}
第三种写法就是自定义类型, 更高级一点,什么都组合起来
type NotgateMathError struct {
lat, long string
err error
}
func (n *NotgateMathError) Error() string {
return fmt.Sprintf("a norgate math error at lat: [%v], long: [%v], because: [%v]", n.lat, n.long, n.err)
}
func Sqrt(f float64) (float64, error) {
if f < 0 {
err := fmt.Errorf("error in number %f", f)
return 0, &NotgateMathError{
lat: "50.2289N",
long: "99.4546",
err: err,
}
}
return 100, nil
}
func main() {
_, err := Sqrt(-9999)
if err != nil {
log.Println(err)
log.Fatal(err)
}
}
这里注意几个点,这是很多个的叠加....还定义了结构体
结构体 NotgateMathError 必须实现 Error 接口,Error 接口这样就可以带上上下文信息,和 error
因为上下文信息,已经带了,所以在fmt.Errorf 的时候,只需要带上error的提示信息即可,最后还是需要
err := fmt.Errorf("error in number %f", f)
的,因为这是一个error类型,输出就是
2024/05/16 00:43:08 a norgate math error at lat: [50.2289N], long: [99.4546], because: [error in number -9999.000000]
现在来看fmt.Errorf 和 log 的error 或者 error new 的区别是什么,以及 fmt.Errorf 是返回一个error 对象的话,那和我 errors.New 一个对象有什么区别
首先回答第二个问题,fmt.Errorf 和 error.New 有什么区别
- 格式化能力:
fmt.Errorf 允许你传递一个格式化字符串和格式化参数,然后生成错误消息,这和fmt.Sprintf 类似,专门为错误消息设计
error.New 不提供格式化功能,他只能接受一个字符串参数,并直接返回这个字符串参数作为错误消息
- 错误检查
fmt.Errorf 创建的对象会包含Errorf的文件和行号信息,这使得他更适合错误链路中的错误包装,因为他保留了原始的上下文错误
error.New 创建的对象通常不具备任何可以用于 errors.Is 或者 errors.As 进行错误检查的有用信息,这是因为 errors.New 返回的错误类型,不包含任何额外的堆栈信息或其他元数据
- 错误包装
当你需要在错误信息中包含变量或者动态信息时,fmt.Errorf 是一个更好的选择,因为他允许你格式化错误消息
如果你只需要返回一个静态的错误消息,那么errors.New 就足够了
- 错误链
在错误链路中 fmt.Errorf 更常用于包装底层错误,因为他包含了调用点的堆栈信息,有助于调试
errors.New 通用用于创建顶层错误,或者在不需要堆栈信息的情况下使用
在实际编程中,选择使用 fmt.Errorf 还是 errors.New 取决于你是否需要格式化错误消息以及你的错误处理需求。如果你需要创建一个带有变量信息的错误,或者在错误链中保留调用点的上下文,fmt.Errorf 是更好的选择。如果你只需要一个简单的、静态的错误消息,errors.New 就足够了。
fmt.Errorf 和 log 中的error有什么区别,回答第一个问题:
fmt.Errorf 格式化一个错误信息并返回一个实现了error接口的新 error对象,这个函数通常用于需要返回自定义错误的场景
函数签名是
func Errorf(format string, a ...interface{}) error {
return errors.New(Sprintf(format, a...))
}
正是因为用的是接口,是一个万能容器,所以放什么信息都是可以的,然后用 err.New 包装了一下,加上了 Sprintf
区别:
- 目的
fmt.Errorf 用于生成一个错误对象,通用用于函数返回值,表示发生了错误 log.Error 或者 log.Errorf 用于记录错误信息到日志文件或者标准输出,他们不会错误对象
- 返回值
fmt.Errorf 返回一个实现了error接口的值
log 的error不返回任何值,他们只负责记录日志
- 使用场景
当你需要在函数中返回一个错误时,使用 fmt.Errorf
当你需要记录一个错误发生的情况,但是并不关心错误是否被处理时,使用log的error
- 接口实现
fmt.Errof 创建的是一个实现了error接口的error对象,可以用errors.Is 或者 errors.As 函数用于错误检查
log 只是负责输出日志信息,不涉及错误接口的实现
总结:如果你需要返回一个错误以供调用者处理,那你返回 fmt.Errorf 如果你只是需要记录信息,那么就使用 log的error
具体Is 和As 的用法是这样的