实验深度理解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. 基于实验进行原理讲解

参考

相关文章
|
3月前
|
JSON 人工智能 Go
go 反射的常见用法
go 反射的常见用法
38 4
|
3月前
|
人工智能 编译器 Go
go slice 基本用法
go slice 基本用法
49 1
|
3月前
|
存储 Go
掌握 Go 语言的 defer 关键字
掌握 Go 语言的 defer 关键字
|
3月前
|
Go 调度 C语言
go语言中一些用法
本文详细解析了Go语言如何通过C语言扩展其功能。主要包括:使用注释书写C代码、通过Go调用C中的函数示例及背后的工作原理;解释了Go如何通过内存结构体传递参数并接收返回值,以及Go调度器如何处理C代码执行期间的控制流问题;讨论了使用cgo的优势与局限性;最后介绍了Go语言中`defer`关键字的工作机制,并通过具体示例展示了反射在动态调用函数中的应用。
|
5月前
|
Unix Shell 编译器
Go 中空结构有什么用法
在 Go 语言中,空结构体 struct{} 是一个非常特殊的类型,它不包含任何字段并且不占用任何内存空间。虽然听起来似乎没什么用,但空结构体在 Go 编程中实际上有着广泛的应用。本文将详细探讨空结构体的几种典型用法,并解释为何它们在特定场景下非常有用。
|
6月前
|
Java Go 区块链
【Go语言专栏】Go语言中的延迟执行与defer语句
【4月更文挑战第30天】Go语言的延迟执行与defer语句用于资源释放和错误处理。defer通过关键字定义,函数返回时执行,顺序与定义相反。参数在定义时求值。应用包括资源释放、错误处理、成对操作和函数包装,是Go编程的关键特性。
51 0
|
编解码 中间件 Go
Golang 中比较常见的 panic 异常原因之一
在 Golang 中,当异常发生时不管是主动触发 panic 还是由于编码错误导致的 panic,我们都可以使用 recover 进行捕获。当时前提必须定义 defer 语句,且 defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。
1257 0
Golang 中比较常见的 panic 异常原因之一
|
6天前
|
存储 前端开发 Go
Go语言中的数组
在 Go 语言中,数组是一种固定长度的、相同类型元素的序列。数组声明时长度已确定,不可改变,支持多种初始化方式,如使用 `var` 关键字、短变量声明、省略号 `...` 推断长度等。数组内存布局连续,可通过索引高效访问。遍历数组常用 `for` 循环和 `range` 关键字。
|
7天前
|
Go 调度 开发者
Go语言中的并发编程:深入理解与实践###
探索Go语言在并发编程中的独特优势,揭秘其高效实现的底层机制。本文通过实例和分析,引导读者从基础到进阶,掌握Goroutines、Channels等核心概念,提升并发处理能力。 ###
|
3天前
|
存储 安全 算法
Go语言是如何支持多线程的
【10月更文挑战第21】Go语言是如何支持多线程的
98 72