实验深度理解Go中try...catch...的panic、defer、recover用法

简介: 文章通过实验代码演示了Go语言中如何使用panic、defer和recover函数来模拟try...catch...的异常处理机制,并详细解释了每个函数的作用和在异常处理中的使用场景。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

Go中如何写try...catch...

go中是没有try...catch...语法的,但是仍提供了实现类似功能的panic、defer、recover函数来实现try...catch...。那么具体该如何实现呢?我们就从一个小目标开启实验之旅。实验目标:异常时正确返回期望的结构体数据

panic、defer、recover实验

1. 设计实验代码

package main

import "fmt"

type TxInfo struct {
    ChainId   string `json:"chainId"`
    Hash      string `json:"hash"`
    BlockHash string `json:"blockHash"`
}

func main() {
    ti := TxInfo{
        ChainId: "1",
        Hash:    "0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f",
    }
    ri := &TxInfo{
        ChainId: "1",
        Hash:    "0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f",
    }
    fmt.Println("Test1 Start:=======================")
    ni := parserValue(ti)
    fmt.Println("ni", ni)
    fmt.Println("ti", ti)

    fmt.Println("Test2 Start:=======================")
    nj := parserValue1(ti)
    fmt.Println("nj", nj)
    fmt.Println("ti", ti)

    fmt.Println("Test3 Start:=======================")
    ti = normalValue(ti)
    fmt.Println("ti", ti)

    fmt.Println("Test4 Start:=======================")
    parserReference(ri)
    fmt.Println("ri", *ri)
}

func normalValue(ti TxInfo) TxInfo {

    //保留的还是初识栈的值
    defer handleExceptionValue(ti)

    ti.BlockHash = "14972319"

    //说明了这不是一个原子操作,进行了成功赋值
    return ti

}

func parserValue(ti TxInfo) TxInfo {

    //保留的还是初识栈的值
    defer handleExceptionValue(ti)

    ti.BlockHash = "14972317"

    //尽管handleExcpetionValue中有return 副本的变量,但是parserValue并没有通过return进行赋值返回,所以parserValue返回ni {  }
    panic("parserValue error")

}

//通过指定返回变量t的方式,就不需要在用return了,因为已经包含了赋值+return功能了
func parserValue1(ti TxInfo) (t TxInfo) {

    //保留的还是初识栈的值
    defer handleExceptionValue(ti) //如果这里是ti的话,handleExceptionValue打印的是当前对象{1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f }
    //defer handleExceptionValue(t) //如果这里是t的话,目前t还是空对象,所以handleExceptionValue打印的是{}
    //defer handleExceptionNoRecover(ti) //因为里面不包含recover()函数,所以整个服务就停止运行;和没有defer的效果是一样的

    //改为变量赋值的方式来返回
    t = ti
    t.BlockHash = "14972311"

    fmt.Println("Inner ti:", ti) // {1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f } 说明是值传递,即t的改变并未改变ti
    fmt.Println("Inner t:", t)   //{1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f 14972311}

    panic("parserValue1 error")

}

//对于引用,无需返回,不惧怕中间繁盛Panic导致的还未赋值,推荐的做法
func parserReference(ri *TxInfo) {

    defer handleExceptionReference("parserReference")

    ri.BlockHash = "14972318"

    panic("parserReference error")

}

func handleExceptionNoRecover(ti TxInfo) TxInfo {
    fmt.Println(ti, "handleExceptionNoRecover-Recover:", ti)
    return ti
}

func handleExceptionValue(ti TxInfo) TxInfo {
    if r := recover(); r != nil {
        fmt.Println(ti, "handleExceptionValue-Recover:", r)
    }
    return ti
}

func handleExceptionReference(funcname string) {
    if r := recover(); r != nil {
        fmt.Println(funcname, "handleExceptionReference-Recover:", r)
    }
}

2. 实验代码运行输出

Test1 Start:=======================
{
   1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f } handleExceptionValue-Recover: parserValue error
ni {
     }
ti {
   1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f }
Test2 Start:=======================
Inner ti: {
   1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f }
Inner t: {
   1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f 14972311}
{
   1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f } handleExceptionValue-Recover: parserValue1 error
nj {
   1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f 14972311}
ti {
   1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f }
Test3 Start:=======================
ti {
   1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f 14972319}
Test4 Start:=======================
parserReference handleExceptionReference-Recover: parserReference error
ri {
   1 0x86a5a6074cce7d1f418b41f363a22e64426dba10039f120832af064c8aa1266f 14972318}

3. 基于实验进行原理讲解

参考

相关文章
|
9月前
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
6月前
|
安全 Go 开发者
Go语言之切片的原理与用法 - 《Go语言实战指南》
切片(slice)是Go语言中用于处理变长数据集合的核心结构,基于数组的轻量级抽象,具有灵活高效的特点。切片本质是一个三元组:指向底层数组的指针、长度(len)和容量(cap)。本文详细介绍了切片的声明与初始化方式、基本操作(如访问、修改、遍历)、长度与容量的区别、自动扩容机制、共享与副本处理、引用类型特性以及常见陷阱。通过理解切片的底层原理,开发者可以更高效地使用这一数据结构,优化代码性能。
161 13
|
6月前
|
安全 Go
defer关键字:延迟调用机制-《Go语言实战指南》
Go 语言中的 `defer` 是用于延迟执行函数调用的关键字,广泛应用于资源释放、异常捕获和日志记录等场景。它在函数返回前执行,支持栈式后进先出(LIFO)顺序,参数求值时机为声明时而非执行时。常见用法包括文件关闭、锁解锁及结合 `recover` 处理 panic。尽管高效,频繁使用可能带来性能开销,需谨慎处理。总结而言,`defer` 是构建健壮代码的核心工具之一。
|
JSON 人工智能 Go
go 反射的常见用法
go 反射的常见用法
133 4
掌握 Go 语言的 defer 关键字
掌握 Go 语言的 defer 关键字
|
人工智能 编译器 Go
go slice 基本用法
go slice 基本用法
141 1
|
1月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
96 1
|
3月前
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
271 1
|
3月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
331 0
|
3月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
217 0