2022/10/01

Recent entries from same category

  1. Go 言語プログラミングエッセンスという本を書きました。
  2. unsafe.StringData、unsafe.String、unsafe.SliceData が入った。
  3. Re: Go言語で画像ファイルか確認してみる
  4. net/url に JoinPath が入った。
  5. Go の struct は小さくできる(fieldalignment のススメ)

errors, fmt: add support for wrapping multiple errors · golang/go@4a0a2b3 · GitHub

An error which implements an "Unwrap() []error" method wraps all the non-nil errors in the returned ...

https://github.com/golang/go/commit/4a0a2b33dfa3c99250efa222439f2c27d6780e4a

Go でエラーを扱う際に、複数のエラーを束ねたい事があります。例えば複数のタスクを実行し、1つでもエラーになれば中断するのではなく、一通りタスクを実施し終えた結果を返したい様なニーズです。

package main

import (
    "errors"
    "log"
    "os"
    "sync"
)

func doMultiTasks(files []stringerror {
    var mu sync.Mutex
    var wg sync.WaitGroup
    var errs []error
    for _, file := range files {
        wg.Add(1)
        go func(file string) {
            defer wg.Done()

            f, err := os.Open(file)
            if err != nil {
                mu.Lock()
                errs = append(errs, err)
                mu.Unlock()
            } else {
                defer f.Close()
                // do something
            }
        }(file)
    }
    wg.Wait()

    return errors.Join(errs...)
}

func main() {
    err := doMultiTasks([]string{"not-found1""not-found2"})
    if err != nil {
        if errs, ok := err.(interface{ Unwrap() []error }); ok {
            for _, e := range errs.Unwrap() {
                log.Println(e)
            }
        } else {
            log.Println(err)
        }
    }
}

このコードは doMultiTasks に処理対象のファイル名を渡し、一通り実施した結果を返します。エラーを束ねるのに errors.Join を使います。束ねたエラーは通常踊り error として扱えます。ただし束ねたエラーを戻す関数は現状用意されていませんが、error を複数返す Unwrap という関数で型アサーションしてやる事で複数のエラーに戻せます。

また fmt.Errorf を使い書式フォーマットに %w を加える事でメッセージとエラーの両方を埋め込む事が出来ますが、本修正により複数の %w を埋め込む事ができる様になりました。

package main

import (
    "fmt"
    "log"
    "os"
)

func main() {
    err := fmt.Errorf("%w and %w", os.ErrNotExist, os.ErrClosed)
    if err != nil {
        if errs, ok := err.(interface{ Unwrap() []error }); ok {
            for _, e := range errs.Unwrap() {
                log.Println(e)
            }
        } else {
            log.Println(err)
        }
    }
}

個人的にはそれほど多いニーズとは思っていませんが、無くはない程度に感じています。

Posted at by