首页 > 解决方案 > 一种不使用最新的 err 值编写 long defer 的方法?

问题描述

我有这个有效的代码。

func (r *repoPG) WithTransaction(txFunc func() error) (err error) {
    tx := db.NewTx()

    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("panic: %v", r)
            tx.Rollback()
        } else if err != nil {
            tx.Rollback()
        } else {
            tx.Commit()
        }
    }()

    err = txFunc()
    return
}

我想避免每次都写这么长defer,所以我试图写一个这样的函数:

func TxDefer(tx, err) {
  if r := recover(); r != nil {
    err = fmt.Errorf("panic: %v", r)
    tx.Rollback()
  } else if err != nil {
    tx.Rollback()
  } else {
    tx.Commit()
  }
}

像这样使用它:

func (r *repoPG) WithTransaction(txFunc func() error) (err error) {
    tx := db.NewTx()

    defer TxDefer(tx, err)

    err = txFunc()
    return
}

但这是非常不正确的,因为err它始终是原始的,而不是 的结果txFunc(),对吧?

我怎样才能解决这个问题?

标签: godeferred

解决方案


将错误的地址传递给函数。这允许函数访问调用者变量的当前值。它还允许函数设置变量。

回滚和提交返回错误。这些错误应该返回给调用者。

func TxDefer(tx Transaction, perr *error) {
  if r := recover(); r != nil {
    *perr = fmt.Errorf("panic: %v", r)
    tx.Rollback()
  } else if *perr != nil {
    err = tx.Rollback()
    if err != nil {
       // replace original error with rollback error
       *perr = err
    }
  } else {
    *perr = tx.Commit()
  }
}

像这样使用它:

func (r *repoPG) WithTransaction(txFunc func() error) (err error) {
    tx := db.NewTx()
    defer TxDefer(tx, &err)
    err = txFunc()
    return
}

在上面的代码中,表达式计算为in*perr的当前值。问题中的值是延迟时的值。errWithTransactionerrerr


推荐阅读