3

Представьте, что Вы открыли коннект к БД, получили курсор и делаете, как обычно:

defer c.Close()

Но в этом коде есть нюанс. Очень часто, Close() возвращает ошибку. В таком случае, мне не ясно, что с ней делать. С одной стороны есть замечательный оператор defer, который мне очень нравится, с другой стороны, мне не хочется делать, как написано тут, ибо такой способ обработки ошибок мешает читать код:

func DoStuff() (s string, err error) { 
    // note the named responses needed for the defer to return an error
    myFile, err := os.Open("filename")
    if err != nil {
        return "", errors.New("Couldn't open file: " + err.Error())
    }

    defer func() {
        if cerr := myFile.Close(); cerr != nil && err == nil {
            err = cerr
        }
    }()

    // Do stuff
    return "someString", nil
}

Я также нашёл вот такую реализацию, но она оказалась нерабочей:

func safeClose(c io.Closer, err *error) {
  if cerr := c.Close(); cerr != nil && *err == nil {
    *err = cerr
  }
}

func WriteFile(filename string, data []byte) (err error) {
  f, err := os.Create(filename)

  if err != nil {
    return err
  }

  defer safeClose(f, &err)

  _, err = f.Write(data)
  return err
}

Я переписал её:

func safeClose(err *error)  {
    *err = errors.New("123")
}

func f() error {
    var err error

    defer safeClose(&err)
    return err
}

func main() {
    e := f()
    fmt.Println(e)
}

Оказалось, что я получаю:

<nil>

Вопросы:

  • Можно ли исправить мой пример?

  • Как поступать в такой истуации: с или без defer?

Ainar-G
  • 16,042
hedgehogues
  • 9,569

1 Answers1

0

Ошибки — это значения. Я на своих личных проектах использую такой шаблон:

// ErrorPair is a pair of errors.
type ErrorPair struct {
    Returned, Deferred error
}

// Error implements the error interface for ErrorPair.
func (err ErrorPair) Error() string {
    return fmt.Sprintf("returned: %s; deferred: %s", err.Returned, err.Deferred)
}

// Unwrap implements the errors.Wrapper interface for ErrorPair.
//
// It returns the Returned error.
func (err ErrorPair) Unwrap() error {
    return err.Returned
}

Используется так:

defer func() {
    cerr := stmt.Close()
    if cerr != nil {
        if err != nil {
            err = ErrorPair{Returned: err, Deferred: cerr}
        } else {
            err = cerr
        }
    }
}()
Ainar-G
  • 16,042