Golang中如何实现try catch功能

Golang中如何实现try catch功能 我正在使用的包没有提供错误信息,例如:

session := db.driver.New(...)

这会抛出错误:

尝试在已关闭的驱动上创建会话

这是因为我已经有意关闭了驱动。但事实是,现在我想对我的会话错误进行 try...catch

try {
  session := db.driver.New(...)
} catch (err) {
  fmt.Println(err)
}

但显然我们没有这样的 try...catch 选项。那么,在这种情况下我们如何进行 try...catch 呢?

如果我记录会话,我得到的是:

&{0x14000020400}

如果我能读取它们,或许就能解决这个问题。但不确定该如何做? 我也尝试了:

fmt.Printf("%v %p\n", &session, session)

但这打印出来像:

0x1400060e030 0x1400060e050

更多关于Golang中如何实现try catch功能的实战教程也可以访问 https://www.itying.com/category-94-b0.html

11 回复

我做不到。它返回的是指针引用,正如我展示的那样。

更多关于Golang中如何实现try catch功能的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


如果没有错误变量但仍想检查错误,请尝试检查返回变量类型的零值。

try-catch 块用于处理程序执行过程中可能发生的异常或错误,它允许你优雅地处理并从未预期的状况中恢复……

这正是我所寻找的。谢谢。

没看到标记为解决方案的选项。

我无能为力。就是这个函数导致了问题,但返回值中没有错误。它只是返回指针地址。如果我能以某种方式获得可读的值,我会很高兴的。

存在这样的可能性,即该函数仅用于分配一些资源,因此基本上不会发生任何导致错误的情况。在这种情况下,您只需检查使用会话处理程序的其他函数调用所产生的错误。

我认为情况更接近这样。假设,我们发起了一个 POST /api/users 请求,而 main.go 正在运行并服务于该 API。我在这里想要的是,当 /api/user.go 在处理某些事情时出错,例如创建用户时出错,我希望能够重新配置 main.go 并再次调用那个函数来尝试创建用户。希望你现在明白我的意图了。

@skillian 你能再详细指导我一下吗?

我想在 main.go 中处理错误,如果包层级的代码引发了某些错误:

/api/user.go
if someCondition {
  // 标记为错误
}
main.go
// 捕获错误
// 执行某些操作

我想我需要使用上下文(context)或协程(goroutine)?我不太确定具体怎么做。

我的观点是,从 panic 中恢复是一个危险信号,表明你正在做错事,而我正试图帮助你找出应对方法。Go 不使用 try ... catch 进行错误处理。要回答你的问题,以下是如何从 panic 中恢复:

session, err := func() (s *Session, err error) {
    defer func() {
        v := recover()
        if v == nil {
            return
        }
        if e, ok := v.(error); ok {
            err = e
            return
        }
        err = fmt.Errorf("%v", v)
        return
    }()
    return db.driver.New(...), nil
}()
if err != nil {
    // 处理 err
}
// 使用 session 进行一些操作

但要么是:

  1. 你使用的 API 是一个糟糕的 API,或者
  2. 你错误地使用了该 API。

我能想到两种实现“包级别”代码的方式:

  1. init 函数:
package main

func init() {
    if someGlobalVariable != expectedValue {
        panic("something is wrong")
    }
}

func main() {
    // `init` 函数会在 `main` 函数被调用前自动运行。
}
  1. 通过调用匿名函数来初始化全局变量:
var someGlobalVariable = func() int {
    v, err := someFunctionThatCanFail()
    if err != nil {
        panic("something failed")
    }
    return v
}()

func main() {
    // 如果你能执行到这里,说明 `someGlobalVariable` 已经被设置好了。
}

我只在一种情况下使用过这两种方法:

var currentUser = func() *user.User {
    me, err := user.Current()
    if err != nil {
        panic(fmt.Errorf("failed to get current user: %w", err))
    }
    return me
}()

func main() {
    defaultConfigPath := filepath.Join(currentUser.HomeDir, ".config", "myApp")
}

如果你正在寻找重试逻辑,你可以直接检查错误并重试最多 n 次。这里有一个使用闭包的示例:

在特定于 HTTP 请求的情况下,你可以使用自定义处理程序来重试一次:

package main

import (
	"log"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.Handle("/user", RetryHandler(CreateUser))
	log.Fatal(http.ListenAndServe(":8090", mux))
}

// Will call f() and retry once if it returns an error
func RetryHandler(f func(w http.ResponseWriter, r *http.Request) error) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		err := f(w, r)
		// We had a problem. Retry the request
		if err != nil {
			err = f(w, r)
		}
		// TODO: add global error handling logic here?
	})
}

func CreateUser(w http.ResponseWriter, r *http.Request) error {
	// Create user and return errors
	return nil
}

这里的注意事项当然是,在确定没有错误之前,你需要在 CreateUser 中不要写入 w。你不希望开始写入 w,然后返回一个错误,这会导致 RetryHandler 再次调用 CreateUser 处理程序。

可能也有用的东西:

  • 你也可以添加 retryCount int 作为 RetryHandler 的参数。
  • 你也可以重构 RetryHandler 使其通用,并处理对象的反序列化/序列化,这样它只需调用一个函数,然后将该函数返回的任何内容发送到 w。自从泛型出现并使这变得容易以来,我以前就做过这个。
  • 你可以添加一个退避延迟,而不是立即重试。
  • 你可以添加全局错误处理,正如我在代码中提到的。

最后,有一些旨在帮助处理重试的包,例如:

你可以试试那个。

回到顶部