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

参考

相关文章
|
1月前
|
JSON 人工智能 Go
go 反射的常见用法
go 反射的常见用法
29 4
|
1月前
|
人工智能 编译器 Go
go slice 基本用法
go slice 基本用法
38 1
|
1月前
|
存储 Go
掌握 Go 语言的 defer 关键字
掌握 Go 语言的 defer 关键字
|
1月前
|
Go 调度 C语言
go语言中一些用法
本文详细解析了Go语言如何通过C语言扩展其功能。主要包括:使用注释书写C代码、通过Go调用C中的函数示例及背后的工作原理;解释了Go如何通过内存结构体传递参数并接收返回值,以及Go调度器如何处理C代码执行期间的控制流问题;讨论了使用cgo的优势与局限性;最后介绍了Go语言中`defer`关键字的工作机制,并通过具体示例展示了反射在动态调用函数中的应用。
|
3月前
|
Unix Shell 编译器
Go 中空结构有什么用法
在 Go 语言中,空结构体 struct{} 是一个非常特殊的类型,它不包含任何字段并且不占用任何内存空间。虽然听起来似乎没什么用,但空结构体在 Go 编程中实际上有着广泛的应用。本文将详细探讨空结构体的几种典型用法,并解释为何它们在特定场景下非常有用。
|
4月前
|
Java Go 区块链
【Go语言专栏】Go语言中的延迟执行与defer语句
【4月更文挑战第30天】Go语言的延迟执行与defer语句用于资源释放和错误处理。defer通过关键字定义,函数返回时执行,顺序与定义相反。参数在定义时求值。应用包括资源释放、错误处理、成对操作和函数包装,是Go编程的关键特性。
39 0
|
编解码 中间件 Go
Golang 中比较常见的 panic 异常原因之一
在 Golang 中,当异常发生时不管是主动触发 panic 还是由于编码错误导致的 panic,我们都可以使用 recover 进行捕获。当时前提必须定义 defer 语句,且 defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。
1233 0
Golang 中比较常见的 panic 异常原因之一
|
8天前
|
程序员 Go PHP
为什么大部分的 PHP 程序员转不了 Go 语言?
【9月更文挑战第8天】大部分 PHP 程序员难以转向 Go 语言,主要因为:一、编程习惯与思维方式差异,如语法风格和编程范式;二、学习成本高,需掌握新知识体系且面临项目压力;三、职业发展考量,现有技能价值及市场需求不确定性。学习新语言虽有挑战,但对拓宽职业道路至关重要。
38 10
|
6天前
|
Go API 开发者
深入探讨:使用Go语言构建高性能RESTful API服务
在本文中,我们将探索Go语言在构建高效、可靠的RESTful API服务中的独特优势。通过实际案例分析,我们将展示Go如何通过其并发模型、简洁的语法和内置的http包,成为现代后端服务开发的有力工具。
|
8天前
|
算法 程序员 Go
PHP 程序员学会了 Go 语言就能唬住面试官吗?
【9月更文挑战第8天】学会Go语言可提升PHP程序员的面试印象,但不足以 solely “唬住” 面试官。学习新语言能展现学习能力、拓宽技术视野,并增加就业机会。然而,实际项目经验、深入理解语言特性和综合能力更为关键。全面展示这些方面才能真正提升面试成功率。
29 10