21天速成go-第八天-2

107 阅读6分钟

需要切换下状态

一直看各种炫技的语法糖和写法很烧脑...需要配合看框架和项目讲解视频来解压下脑子,不然会有厌烦心理的

另外发现 每日英语听力 的打卡还蛮好使,相当于给定了一个需要完成的目标,每天都能够得到,也比看花时间的掘金长文解压一点

后面还有几个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
}

必须要写成这样才行,不能写成

image.png

那么还不如不用命名返回值

  • 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 有什么区别

  1. 格式化能力:

fmt.Errorf 允许你传递一个格式化字符串和格式化参数,然后生成错误消息,这和fmt.Sprintf 类似,专门为错误消息设计

error.New 不提供格式化功能,他只能接受一个字符串参数,并直接返回这个字符串参数作为错误消息

  1. 错误检查

fmt.Errorf 创建的对象会包含Errorf的文件和行号信息,这使得他更适合错误链路中的错误包装,因为他保留了原始的上下文错误

error.New 创建的对象通常不具备任何可以用于 errors.Is 或者 errors.As 进行错误检查的有用信息,这是因为 errors.New 返回的错误类型,不包含任何额外的堆栈信息或其他元数据

  1. 错误包装

当你需要在错误信息中包含变量或者动态信息时,fmt.Errorf 是一个更好的选择,因为他允许你格式化错误消息

如果你只需要返回一个静态的错误消息,那么errors.New 就足够了

  1. 错误链

在错误链路中 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

区别:

  1. 目的

fmt.Errorf 用于生成一个错误对象,通用用于函数返回值,表示发生了错误 log.Error 或者 log.Errorf 用于记录错误信息到日志文件或者标准输出,他们不会错误对象

  1. 返回值

fmt.Errorf 返回一个实现了error接口的值

log 的error不返回任何值,他们只负责记录日志

  1. 使用场景

当你需要在函数中返回一个错误时,使用 fmt.Errorf

当你需要记录一个错误发生的情况,但是并不关心错误是否被处理时,使用log的error

  1. 接口实现

fmt.Errof 创建的是一个实现了error接口的error对象,可以用errors.Is 或者 errors.As 函数用于错误检查

log 只是负责输出日志信息,不涉及错误接口的实现

总结:如果你需要返回一个错误以供调用者处理,那你返回 fmt.Errorf 如果你只是需要记录信息,那么就使用 log的error

具体Is 和As 的用法是这样的

image.png