Go进阶笔记关于Error

golang error处理 golang error处理error 是什么?存在的问题舍弃的try提案社区对于error堆栈解决方案go 1.13 的Error 升级 本文主要讲解golang中error的使用以及原理。主要包括以下话题。 error是什么 error 面临的问题 舍弃的try提案 社区对于error堆栈解决方案 go 1.13 对于error的提升 error 是什么? 在 golang 中我们使用 e... 阅读详情

很多人对于Go的error比较吐槽,说代码中总是会有大量的如下代码:

if err != nil {
    ...
}

其实很多时候是使用的姿势不对,或者说,对于error的用法没有完全理解,这里整理一下关于Go中的error 。

关于源码中的error

先看一下go源码中go/src/builtin/builtin.go对于error的定义:

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
        Error() string
}

我们使用的时候经常会通过errors.New() 来返回一个error对象,这里可以看一下我们调用errors.New()的这段源码文件go/src/errors/errors.go,可以看到errorString实现了error解接口,而errors.New()其实返回的是一个 &errorString{text} 即errorString对象的指针。

package errors

// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
    return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
    s string
}

func (e *errorString) Error() string {
    return e.s
}

如果之前看过一些优秀源码或者go源码的,会发现代码中通常会定义很多自定义的error,并且都是包级别的变量,即变量名首字母大写:

// https://golang.org/pkg/bufio


var (
    ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
    ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
    ErrBufferFull        = errors.New("bufio: buffer full")
    ErrNegativeCount     = errors.New("bufio: negative count")
)

注意:自己之后在代码中关于这种自定义错误的定义,也要参照这种格式规范定义。
“当前的包名:错误信息”

package main

import (
    "errors"
    "fmt"
)

type errorString string

// 实现 error 接口
func (e errorString) Error() string {
    return string(e)
}

func New(text string) error {
    return errorString(text)
}

var errNamedType = New("EOF")
var ErrStructType = errors.New("EOF")

func main() {
    //  这里其实就是两个结构体值的比较
    if errNamedType == New("EOF") {
        fmt.Println("Named Type Error")   // 这行打印会输出
    }
    // 标准库中errors.New() 返回的是一个地址,每次调用都会返回一个新的内存地址
    // 标准库这样设计也是为了避免碰巧如果两个结构体值相同了,而引发一些不期望的问题
    if ErrStructType == errors.New("EOF") {
        fmt.Println("Struct Type Error")  // 这行打印不会输出
    }
}

关于结构体值的比较:

如果两个结构体值的类型均为可比较类型,则它们仅在它们的类型相同或者它们的底层类型相同(要考虑字段标签)并且其中至少有一个结构体值的类型为非定义类型时才可以互相比较。

如果两个结构体值可以相互比较,则它们的比较结果等同于逐个比较它们的相应字段。

注意:关于Go中函数支持多参数返回,如果函数有error的通常把返回值的最后一个参数作为error

如果一个函数返回(value, error)这个时候必须先判定error
Go中的panic 意味着程序挂了不能继续运行了,不能假设调用者来解决panic。

对于刚学习go的时候经常用如下代码开启一个goroutine执行任务:

go func() {
    ...
}

这种情况也叫野生goroutine,并且这个时候recover是不能解决的。

可以定义一个包,通过调用该包中的Go() 方法来开goroutine,来避免野生goroutine。

package sync

func Go(x func()) {

    if err := recover(); err != nil {
        ....
    }
    go x()
}

关于代码的panic 通常在代码中是很少使用的,只有在极少情况下,我们需要panic,如我们项目的初始化地方连接数据库连接不上,并且这个时候,数据库是我们程序的强依赖,那么这个时候是可以panic。

下面通过一个例子来演示error的使用姿势:

package main

import (
    "errors"
    "fmt"
)

// 判断正负数
func Positivie(n int) (bool, error) {
    if n == 0 {
        return false, errors.New("undefined")
    }
    return true, nil
}

func Check(n int) {
    pos, err := Positivie(n)
    if err != nil {
        fmt.Println(n, err)
        return
    }
    if pos {
        fmt.Println(n, "is positive")
    } else {
        fmt.Println(n, "is negative")
    }
}

func main() {
    Check(1)
    Check(0)
    Check(-1)
}

上面是一种非常正确的姿势,我们通过返回(value, error) 这种方式来解决,也是非常go 的一种写法,只有err!=nil 的时候我们的value才有意义

那么在实际中可能有很多各种姿势来解决上述的问题,如下:

package main

import "fmt"

func Positive(n int) *bool {
    if n == 0 {
        return nil
    }
    r := n > -1
    return &r
}

func Check(n int) {
    pos := Positive(n)
    if pos == nil {
        fmt.Println(n, "is neither")
        return
    }
    if *pos {
        fmt.Println(n, "is positive")
    } else {
        fmt.Println(n, "is negative")
    }
}

func main() {
    Check(1)
    Check(0)
    Check(-1)
}

另外一种姿势:

package main

import "fmt"

func Positive(n int) bool {
    if n == 0 {
        panic("undefined")
    }
    return n > -1
}

func Check(n int) {
    defer func() {
        if recover() != nil {
            fmt.Println("is neither")
        }
    }()

    if Positive(n) {
        fmt.Println(n, "is positive")
    } else {
        fmt.Println(n, "is negative")
    }
}

func main() {
    Check(1)
    Check(0)
    Check(-1)
}

上面这两种姿势虽然也可以实现这个功能,但是非常的不好,也不推荐使用。在代码中尽可能还是使用(value, error) 这种返回值来解决error的情况。

对于真正意外的情况,那些不可恢复的程序错误,例如索引越界,不可恢复的环境问题,栈溢出等才会使用panic,对于其他的情况我们应该还是期望使用error来进行判定。

error 处理套路

Sentinel Error 预定义error

通常我们把代码包中如下的这种error叫预定义error.

// https://golang.org/pkg/bufio


var (
    ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
    ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
    ErrBufferFull        = errors.New("bufio: buffer full")
    ErrNegativeCount     = errors.New("bufio: negative count")
)

这种姿势的缺点:

  • 对于这种错误,在实际中的使用中我们通常会使用 if err == ErrSomething {....} 这种姿势来进行判断。但是也不得不说,这种姿势是最不灵活的错误处理策略,并且不能对于错误提供有用的上下文。

  • Sentinel errors 成为API的公共部分。如果你的公共函数或方法返回一个特定值的错误,那么该错误就必须是公共的,当然要有文档记录,这最终会增加API的表面积。

  • Sentinel errors 在两个包之间创建了依赖。对于使用者不得不导入这些错误,这样就在两个包之间建立了依赖关系,当项目中有许多类似的导出错误值时,存在耦合,项目中的其他包必须导入这些错误值才能检查特定的错误条件。

Error types

Error type 是实现了error接口的自定义类型,例如MyError类型记录了文件和行号以展示发生了什么

type MyError struct {
    Msg string
    File string
    Line int
}

func (e *MyError) Error() string {
    return fmt.Sprintf("%s:%d:%s", e.File,e.Line, e.Msg)
}

func test() error {
    return &MyError("something happened", "server.go", 11)
}

func main() {
    err := test()
    switch err := err.(type){
    case nil:
        // ....
    case *MyError:
        fmt.Println("error occurred on line:", err.Line)
    default:
        // ....
    }
}

这种方式其实在标准库中也有使用如os.PathError

// https://golang.org/pkg/os/#PathError

type PathError struct {
    Op   string
    Path string
    Err  error
}

调用者要使用类型断言和类型switch,就要让自定义的error变成public,这种模型会导致和调用者产生强耦合,从而导致API变得脆弱。

Opaque errors

这种方式也称为不透明处理,这也是相对来说比较优雅的处理方式,如下

func fn() error {

    x, err := bar.Foo()
    if err != nil {
        return err
    }
    // use x
}

这种不透明的实现方式,一种比较好的用法,这里以net库的代码来看:

// https://golang.org/pkg/net/#Error

type Error interface {
    error
    Timeout() bool   // Is the error a timeout?
    Temporary() bool // Is the error temporary?
}

这里是定义了一个Error接口,而让其他需要用到error的来实现这个接口,如net中的下面这个错误

// https://golang.org/pkg/net/#DNSConfigError

type DNSConfigError
    func (e *DNSConfigError) Error() string
    func (e *DNSConfigError) Temporary() bool
    func (e *DNSConfigError) Timeout() bool
    func (e *DNSConfigError) Unwrap() error

按照这个方式实现我们使用net时的异常处理可能就是如下情况:

if neerr, ok := err.(net.err); ok && nerr.Temporary() {
    time.Sleep(time.Second * 10)
    continue
}
if err != nil {
    log.Fatal(err)
}

其实这样还是不够优雅,好的方式是我们卡一定义temporary的接口,然后取实现这个接口,这样整体代码就看着非常简洁清楚,对外我们就只需要暴露IsTemporary方法即可,而不用外部再进行断言。

Type temporary interface {
    Temporary() bool
}

func IsTemporary(err error) bool {
    te, ok := err.(temporary)
    return ok && te.Temporary()
}

以上这几种姿势,其实各有各的用处,不同的场景,选择可能也不同,需要根据实际场景实际分析。

一个error 技巧使用例子

先看一段代码,相信这段代码如果很多人实现的时候也都是这个样子:

type Header struct {
    Key, Value string
}

type Status struct {
    Code    int
    Reason string
}

func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {

    _, err := fmt.Fprintf(w, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)
    if err != nil {
        return err
    }

    for _, h := range headers {
        _, err := fmt.Fprintf(w, "%s:%s\r\n", h.Key, h.Value)
        if err != nil {
            return err
        }
    }

    if _, err := fmt.Fprint(w, "\r\n"); err != nil {
        return err
    }

    _, err = io.Copy(w, body)
    return err
}

看这段代码时候估计很多就开始吐嘈go的error的处理,感觉代码中会存在很多err的判断处理,其实这里是可以写的更优雅一点的,上面的姿势不对,来换个姿势:

type errWriter struct {
    io.Writer
    err error
}

func(e *errWriter) Write(buf []byte) (int, error) {
    if e.err != nil {
        return 0, e.err
    }

    var n int
    n, e.err = e.Writer.Write(buf)
    return n,nil
}

func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {
    ew :=&errWriter{Writer:w}
    fmt.Fprintf(ew, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)

    for _, h := range headers {
        fmt.Fprintf(ew, "%s:%s\r\n", h.Key, h.Value)
    }

    fmt.Fprint(w, "\r\n")

    io.Copy(w, body)
    return ew.err
}

对比之下这种代码看起来是不是就非常简洁,所有很多时候可能是自己写代码的姿势不对,而不是go的error设计的不好。

Wrap errors

就像下面这段代码一样,这样的使用方式,我自己在工程代码中也经常看到,这样就会导致生成的错误没有file:line信息,没有导致错误的调用堆栈信息,如果出现异常就非常不方便排查到底是哪里导致的问题,其次因为这里通过fmt.Errorf对错误进行了包装,也就破坏了原始错误。

func AuthenticateReuest(r *Request) error {
    err := authenticate(r.User)
    if err != nil {
        return fmt.Errorf("authenticate failed:%v", err)
    }
    return nil
}

关于error的处理中还有一个非常重要的地方就是是否是每次出现err!=nil的时候,我们都需要打印日志?如果这样做了,你会发现到处在打印日志,还有很多地方可能打印的是相同的日志。

func WriteAll(w io.Writer, buf[]byte) error {
    _, err := w.Write(buf)
    if err != nil {
        log.Println("unalbe to write:",err)  //这里记录了日志
        return err                           //将日志进行上抛给调用者
    }
    return nil
}

func WriteConfig(w io.Writer, conf *Config) error {
    buf, err := json.Marshal(conf)
    if err != nil {
        log.Printf("cound not marshal config:%v", err)
        return err
    }
    if err := WriteAll(w, buf); err != nil {
        log.Println("cound not write config:%v",err)
        return err
    }
    return nil
}

在上面这个例子中, 这个错误逐层返回给调用者,如果处理不好,可能就像上面这个例子,每次都打印日志,一直到程序的顶部
所以:error应该只被处理一次。
Go中错误的处理契约规定:在出现错误的情况下,不能对其他返回值的内容做任何假设,如下代码中,由于json序列化失败,buf的内容是未知的,这个时候把损坏的buf传给后续处理逻辑,这样就会导致一些未知的错误发生。

func WriteConfig(w io.Writer, conf *Config) error {
    buf, err := json.Marshal(conf)
    if err != nil {
        log.Printf("cound not marshal config:%v", err)
        // 忘记return
    }
    if err := WriteAll(w, buf); err != nil {
        log.Println("cound not write config:%v",err)
        return err
    }
    return nil
}

关于错误日志处理的规则:

  • 错误要被日志记录

  • 应用程序处理错误,保证100%的完整性

  • 之后不再报告当前错误

github.com/pkg/errors 这个error处理包非常受欢迎,看一下这个包对错误的处理例子:

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "path/filepath"

    "github.com/pkg/errors"
)

func ReadFile(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, errors.Wrap(err, "open failed")
    }
    defer f.Close()
    buf, err := ioutil.ReadAll(f)
    if err != nil {
        return nil, errors.Wrap(err, "read failed")
    }
    return buf, nil
}

func ReadConfig() ([]byte, error) {
    home := os.Getenv("HOME")
    config, err := ReadFile(filepath.Join(home, ".settings.xml"))
    return config, errors.WithMessage(err, "cound not read config")
}

func main() {
    _, err := ReadConfig()
    if err != nil {
        fmt.Printf("original err:%T %v\n", errors.Cause(err), errors.Cause(err))
        fmt.Printf("stack trace:\n %+v\n",err)  // %+v 可以在打印的时候打印完整的堆栈信息
        os.Exit(1)
    }
}

执行结果如下:

original err:*os.PathError open /Users/zhaofan/.settings.xml: no such file or directory
stack trace:
 open /Users/zhaofan/.settings.xml: no such file or directory
open failed
main.ReadFile
        /Users/zhaofan/open_source_study/test_code/202012/wrap_errors/main.go:15
main.ReadConfig
        /Users/zhaofan/open_source_study/test_code/202012/wrap_errors/main.go:27
main.main
        /Users/zhaofan/open_source_study/test_code/202012/wrap_errors/main.go:32
runtime.main
        /Users/zhaofan/app/go/src/runtime/proc.go:204
runtime.goexit
        /Users/zhaofan/app/go/src/runtime/asm_amd64.s:1374
cound not read config
exit status 1

从代码上也非常简洁,处理的非常优雅,最终不管是错误信息还是堆栈信息,还可以添加自定义的上下文,同时也完全满足上面提出的关于错误日志处理的规则。
关于代码中的Wrap源码如下:

// Wrap returns an error annotating err with a stack trace
// at the point Wrap is called, and the supplied message.
// If err is nil, Wrap returns nil.
func Wrap(err error, message string) error {
    if err == nil {
        return nil
    }
    err = &withMessage{
        cause: err,
        msg:   message,
    }
    return &withStack{
        err,
        callers(),
    }
}

可以看到我们每次调用errors.Wrap方法的时候都是把我们的错误信息err存入到withMessage结构体的cause字段,同时又把包装的withMessage 作为err存到withStack结构体中,同时withStack包含了调用堆栈的信息

type withMessage struct {
    cause error
    msg   string
}

关于github.com/pkg/errors使用姿势

  • 在你自己的应用程序中,使用errors.New或者errors.Errorf返回错误

  • 如果调用其他包内的函数或者你当前项目里的其他函数,通常简单的直接返回,即直接return err

  • 如果你使用第三方库如github库,公司的基础库,或者go的基础库,这个时候应该使用errors.Wrap或者errors.Wrapf保存堆栈信息,同时添加自定义的上下文信息

  • 直接返回错误,而不是每个错误产生的地方打日志

  • 在程序的顶部或者工作的goroutine顶部(请求入口)使用%+v把堆栈详情记录

  • 使用errors.Cause 获取root error即根因,在进行和sentinel error进行等值判定

  • 一旦错误被处理,包括你打印日志,或者降级处理等,这个时候你就不应该再向上抛出err,而应该return nil.

go1.13 中的errors

go 1.13 为errors和fmt标准库引入了新的特性,以简化处理包含其他错误的错误。其中最重要的就是:包含一个错误的error可以实现返回底层错误的Unwrap 方法。如果e1.Unwrap() 返回e2, 那么e1就包装了e2,就可以展开e1以获取e2

在Go的1.13 中fmt.Errorf支持新的%w ,这样就在错误信息中带入原始的信息,这样既保证了人阅读的方便,也方便了机器处理,如:

if err != nil {
    return fmt.Errorf("access denied %w", ErrrPermission)
}

把之前的例子进行调整如下:

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "path/filepath"

    "errors"
)


func ReadFile(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, fmt.Errorf("open failed: %w", err)
    }

    defer f.Close()
    buf, err := ioutil.ReadAll(f)
    if err != nil {
        return nil, fmt.Errorf("read failed: %w", err)
    }
    return buf, nil
}


func ReadConfig() ([]byte, error) {
    home := os.Getenv("HOME")
    config, err := ReadFile(filepath.Join(home, ".settings.xml"))
    return config, fmt.Errorf("cound not read config: %w", err)
}

func main() {
    _, err := ReadConfig()
    if err != nil {
        // errors.Is会一层一层的展开,找最内层的err
        fmt.Println(errors.Is(err, os.ErrNotExist))
        os.Exit(1)
    }
}

但是1.13的errors有个非常大的问题就是不支持携带堆栈信息,所以最好的办法就是把标准库中的errors和github.com/pkg/errors

package main

import (
    "errors"
    "fmt"

    xerrors "github.com/pkg/errors"
)

var errMy = errors.New("My Error")

func test0() error {
    return xerrors.Wrapf(errMy, "test0 failed")
}

func test1() error {
    return test0()
}

func test2() error {
    return test1()
}

func main() {
    err := test2()
    fmt.Printf("main: %+v\n", err)
    fmt.Println(errors.Is(err, errMy))
}

其实原则就是我们底层的错误还是通过 github.com/pkg/errors 中Wrapf 进行包装。并且这个时候也完全兼容标准库中的errors,可以使用errors.Is 和 errors.As方法做判断处理。

Golang中error的一个坑 err != nil 第一个问题是:为什么值为 nil 的 error 却不等于 nil? 思考如下代码: package main import ( "errors" "fmt" ) type Err struct { err string } func (e *Err) Error() string { return e.err } func ... 阅读详情

相关推荐

Go语言(golang)的错误(error)处理的方案

对于Go语言(golang)的错误设计,相信很多人已经体验过了,它是通过返回值的方式,来强迫调用者对错误进行处理,要么你忽略,要么你处理(处理也可以是继续返回给调用者),对于golang这种设计方式,我们会在代码中写大量的if判断,以便做出决定。 func main() { conent,err:=ioutil.ReadFile("filepath") if err !=nil{ //错误...

yonggeit的博客 1万+

【go语言】error 错误处理详解

上述的哨兵错误无法携带上下文信息,使用自定义的错误类型可以包装底层错误携带更多的上下文信息。让我们看一下底层的 os.PathError 是怎么做的Op stringErr errorif err!= nil {ok {} else {运行结果如下图所示:它提供了底层执行了什么操作、哪个路径出了什么问题以及错误消息。这种方式相对于哨兵来说,虽然能够携带更多的上下文信息,但是同样需要调用者进行类型断言,存在强耦合。

Candy___i的博客 3501

【Golang】深入解读Go语言中的错误(error)与异常(panic)

在Go语言编程中,错误和异常处理是非常重要的一部分。Go语言通过其独特的错误处理机制和异常处理模型,提供了一种既清晰又高效的方式来处理程序运行时的错误和异常情况。

景天科技苑 3万+

go中如何处理error

go 中的异常处理和其他语言大不相同,像 Java、C++、python 等语言都是通过抛出 Exception 来处理异常,而 go 是通过返回 error 来判定异常,并进行处理。在 go 中有 panic 的机制,但 panic 意味着程序终止,代码不能继续运行了,不能期望调用者来解决它。而 error 是预期中的异常,希望调用者可以对其进行处理的。其实就是先预定义一些可以预料中的错误,在使用过程中,通过判断 error 是属于哪一种 error 并进行对应的处理。

Crazy博客 2555

CocosCreator3.8.2实战:5分钟搞定多分辨率适配与横竖屏自动翻转(附完整Demo)

本文针对CocosCreator3.8.2开发者,提供了一套5分钟快速集成、开箱即用的多分辨率屏幕适配与横竖屏自动翻转解决方案。通过封装好的ScreenAdapter组件,开发者无需处理复杂计算,即可实现等比缩放或拉伸全屏两种主流适配模式,并自动响应设备旋转,有效解决移动端UI在不同屏幕比例和方向下的显示问题。文末附完整Demo。

z4a5b6的博客 723

一生一芯 预学习阶段 NEMU代码学习(2)

如果 nemu_state.state == NEMU_ABORT:说明程序处于 "中止" 状态,输出 "ABORT",并且使用 ANSI_FG_RED 红色格式化显示。如果 halt_ret == 0,表示程序正常终止(触发了 "GOOD TRAP"),输出 "HIT GOOD TRAP",并使用 ANSI_FG_GREEN 绿色显示。如果要执行的指令数量 n 小于预定义的常量 MAX_INST_TO_PRINT,则将 g_print_step 置为 true,表示会逐步打印指令执行的过程。

weixin_45041054的博客 826

3种方式!Go Error处理最佳实践

导语|错误处理一直以一是编程必需要面对的问题,错误处理如果做的好的话,代码的稳定性会很好。不同的语言有不同的出现处理的方式。Go语言也一样,在本篇文章中,我们来讨论一下Go语言的错误处理方式。一、错误与异常(一)Error错误是程序中可能出现的问题,比如连接数据库失败,连接网络失败等,在程序设计中,错误处理是业务的一部分。Go内建一个error接口类型作为go的错误标...

QcloudCommunity的博客 4726

dify案例-基于Dify打造智能合同审查助手:零代码搭建工作流全指南

Dify的工作流(Workflow)功能是一个可视化流程编排工具,允许用户通过拖拽节点的方式构建复杂的AI应用。与简单的问答应用不同,工作流支持多步骤任务处理、条件分支、并行执行等高级功能,非常适合合同审查这类结构化任务。可视化编排:无需编写代码,通过拖拽连接节点即可构建复杂流程多模型协作:可在不同节点使用最适合的AI模型灵活集成:支持知识库检索、条件判断、API调用等多种节点类型迭代优化:可随时测试和调整单个节点,不影响整体流程可视化搭建:无需编码经验,拖拽式构建复杂审查流程灵活扩展。

weixin_44245188的博客 1939

Go错误处理实践

Go标准库中error类型是一个接口类型,在}只要实现了Error()方法的类型都可以作为error返回,它将错误消息以字符串的形式返回。

张氏小毛驴的博客 1670

手把手教你学Simulink——基于传统电网建模与稳定性分析的场景实例:基于同步发电机的单机无穷大系统建模与稳定性分析

本文详细介绍了使用MATLAB/Simulink进行单机无穷大系统(SMIB)建模与稳定性分析的方法。教程从系统结构设计入手,包含同步发电机、输电线路、无穷大母线等核心模块的建模步骤和参数配置示例。重点讲解了七个关键步骤:项目创建、模块配置、控制系统集成、负载添加(可选)以及仿真运行与结果分析,并提供了具体的MATLAB代码示例。最后通过性能评估指标(功角差、电压跌落、频率偏差)分析系统稳定性,为电力系统动态特性研究提供了实用指导,强调准确参数设置对仿真结果的重要性。

MHD0815的博客 298

AUTOSAR模式管理-EcuM多核处理及其他概念

因此,EcuM不请求状态。因此,BswM必须确保,例如,在调用EcuM_GoHalt或EcuM_GoPoll之前,必须将关机目标从其默认值更改为ECUM_STATE_SLEEP。对于ECU的关闭,BswM(位于主EcuM的同一个分区中)最终调用主核上的GoOff,该GoOff将请求分发给所有的从核。在ECU Manager模块的早期版本中,睡眠目标被特殊处理,因为在ECU中实现的睡眠模式取决于ECU的功能。在使用了外部的SBC控制相关的MCU的下电的时候,在OFF阶段最后调用SBC进入到Sleep的指令。

m0_49148264的博客 1340

【go学习笔记】Go errors 最佳实践

预定义的特定错误,我们叫为 sentinel error,这个名字来源于计算机编程中使用一个特定值来表示不可能进行进一步处理的做法。所以对于 Go,我们使用特定的值来表示错误。类似的 io.EOF,更底层的 syscall.ENOENT。使用 sentinel 值是最不灵活的错误处理策略,因为调用方必须使用 == 将结果与预先声明的值进行比较。当您想要提供更多的上下文时,这就出现了一个问题,因为返回一个不同的错误将破坏相等性检查。

zhuiyisinian的博客 1083

PCIe Link Training 深度解析:从物理检测到高速数据传输

本文深入解析PCIe链路训练(Link Training)的技术细节,揭示了这一复杂协商过程的五个关键阶段:物理检测阶段通过电气特性识别设备连接;轮询阶段建立时钟和符号同步;配置阶段确定链路宽度并处理拓扑异常;恢复阶段完成速率切换和信号均衡;最终进入正常工作状态。文章特别强调了高速率下的均衡技术挑战,以及验证工程师需要关注的多个维度,包括状态机跳转、有序集内容、均衡算法和错误恢复能力。随着PCIe速率不断提升,理解这一底层机制对高速接口设计具有重要价值。

元直的博客 1655

【Golang笔记03】error、panic、fatal错误处理学习笔记

Go语言中的错误处理机制与Java等语言的异常处理有所不同。Go通过返回值来处理错误,主要分为三类:error、panic和fatal。error是部分流程错误,不会导致程序停止;panic是严重错误,程序会立即退出;fatal是致命错误,程序立即停止。Go中没有try...catch语句,错误通过返回值传递,通常使用if语句进行判断。error接口只有一个Error()方法,可以通过errors.New()或fmt.Errorf()创建错误。Go还支持自定义错误类型,只需实现Error()方法。error

✅ Java 开发工程师 1630

Arduino与Proteus仿真实例-LCD2004-I2C(PCF8574)驱动仿真

LCD2004-I2C(PCF8574)驱动仿真 PCF8574/74A 通过两线双向 I2C 总线(串行时钟 (SCL)、串行数据 (SDA))提供通用远程 I/O 扩展。 LCD2004,即2004字符型液晶显示器,是一种点阵模块,用于显示字母、数字、字符等。 它由 5x8 个点阵位置组成; 每个位置可以显示一个字符。 两个字符之间有一个点间距,行之间有一个空格,从而将字符和行分开。 模型 2004 表示它显示 4 行,每行 20 个字符。 在前面的实例中对PCF8574和LCD2004的已经做了详细介

视觉与物联智能 1165

Microsemi Libero系列教程(四)——PLL的使用

文章目录PLL是什么Libero中PLL的使用官方文档 系列教程:Microsemi Libero系列教程 PLL是什么 PLL(Phase Locked Loop): 为锁相回路或锁相环,用来统一整合时钟信号,使高频器件正常工作,如内存的存取资料等。PLL用于振荡器中的反馈技术。 许多电子设备要正常工作,通常需要外部的输入信号与内部的振荡信号同步。一般的晶振由于工艺与成本原因,做不到很高的频率...

whik1194的博客 7212

【Golang学习之旅】Go 语言错误处理(error 接口、panic、recover)

✅ 适合人群:Golang 开发者 | 后端开发工程师 | 并发编程初学者✅ 文章亮点:深入解析 error、panic、recover + 代码示例 + 实战场景✅ 目标:掌握 Go 语言错误处理机制,编写健壮、高质量的 Go 代码!error🔹 Go 语言错误处理的基本原则:✅ 普通错误使用error处理(推荐方式)✅ 严重错误使用panic处理✅ 程序应尽可能优雅地恢复panic用于表示程序发生严重错误,无法继续执行,通常用于不可恢复的错误,如:✅ 数组越界✅ 空指针引用。

程序员林北北的博客 1278

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

谈谈Golang的Error

总结一:Golang的Error \ 这个包是golang内置的error的包,errors包相关源码: package errors // New returns an error that formats as the given text. // Each call to New returns a distinct error value even if the text is identical. func New(text string) error { return &erro

t13233543678的博客 4082

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Golang学习笔记_20——error

Code intCode: 1,测试方法demo_2()输出结果code:1PASS。

LuckyLay的博客 782

Golang基础笔记十二之defer、panic、error

本文介绍了Golang中的defer、panic和error三个关键概念。defer用于延迟执行函数,保证资源释放,按照后进先出顺序执行。panic用于处理不可恢复错误,可通过recover捕获。error是标准错误接口,通常作为函数最后一个返回值,使用errors.New()创建错误信息。文章通过代码示例详细说明了它们的使用场景和注意事项,包括defer的参数传递、panic的捕获处理以及error的标准使用方式,帮助开发者更好地处理程序中的异常情况。

weixin_43354181的博客 1173

Go进阶-Error

文章目录Error vs. ExceptionErrorError vs ExceptionError TypeSentinel ErrorError TypeOpaque errorsHandling ErrorIndented flow is for errorsEliminate error handling by eliminating errorsWrap ErrorsGo 1.13 errorsErrors before Go 1.13UnwrapWrapping errors with %w

somia的博客 818

【Go进阶训练营】Go语言实践-error

go error

芝兰生于深谷,不以无人而不芳 1333

Golang 中 Error 的设计及最佳实践

Go 中的 error 就是一个普通的值对象,而其他语言如 Java 中的 Exception 将会造成程序控制流的终止和其他行为,Exception 与普通的值不同。即:因为异常会将错误的描述和处理错误的控制流纠缠在一起,通常会导致程序错误以一种难以理解的栈追踪的方式被报告给终端用户,这种方式充满了程序结构的信息,但是缺少关于哪里出错的易于理解的上下文信息。Tips: 建议在底层只需要返回清晰地错误信息,每一层包裹一些重要并且简洁的上下文信息,并且最终在程序的顶层或者某一个不得不处理的层级处理该错误。

ldxxxxll的博客 1070

Golang Error Convert To String

学习 golang 的过程中,会在很多情况下接触 error,实际上是 interface 类型,下面是它的定义: type error interface { Error() string } Go 语言的接口设计是非侵入式的,只要实现对应 interface 的方法就可以,所以只要实现 Error() 方法的类型都属于 error 接口类型。 创建 error 最简单的方法就...

mark_stray 3696

golang Error的一些坑

Go错误处理需规范预定义错误变量(如var ErrX = errors.New("x")),避免动态生成导致errors.Is失效;自定义错误应封装结构体并仅暴露接口,返回nil时确保接口类型为nil,防止非预期判断。优先使用errors.Is/As进行链式检查,规避指针和nil陷阱。

qq_的博客 1212
上一篇: 数据包分析基础
下一篇: Elasticsearch启动常见问题
后场技术
博客等级 码龄11年 156粉丝 46原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值