Go言語で、errorを順に投げ上げる場合の問題

https://qiita.com/yoshinori_hisakawa/items/15bf0307245744deb4fc
以下のような問題がある。この問題への回答が言語の機能としてサポートされていない。

errorから呼び出し階層を得られる保証がない

errorは単なる戻り値であるため、main関数→関数A→関数Bという呼び出しをしているとき、関数Bでerrorが発生した場合、以下のようなコードだとスタックトレースが分からない。

func main() {
  result, err = A()
  if err != nil {
    //どういう呼び出しで発生したerrなのか分からない!
  }
}

func A() (string, error) {
  result, err = B();
  if err != nil {
    return "", err
  }
  return string, nil
}

func B() (string, error) {
  if is成功 {
    return "result", nil
  } else {
    return "", error.New("func B err!")
  }
}

それをうまく扱うためにerrorsというパッケージがあり、呼び出し階層ごとのエラーのラッピングと、そこから本来のエラーを取り出す仕組みが提供されている。

func main() {
  result, err = A()
  if err != nil {
    fmt.Println("err info:", err)   // これでスタックトレースが表示される
    originalErr = errors.Cause(err) // 本来のエラーを取り出す
  }
}

func A() (string, error) {
  result, err = B();
  if err != nil {
    return "", errors.Wrap(err, "func A err!") // ラッピング
  }
  return string, nil
}

func B() (string, error) {
  if is成功 {
    return "result", nil
  } else {
    return "", errors.New("func B err!")
  }
}

errorとしてどのような値を戻すべきか

複数種類のエラーが戻る可能性がある場合、どの種類のエラーが戻されたかを判定する方法がいくつかある。
下にいくほど、コードとしては正しいがめんどくさい。
1.「エラー値 errors.New()で作成される値」を戻す。
2.「エラー型 errorインターフェースを実装した型」を戻す。型の違いで判定する。
3.「エラー型が実装したインターフェース」の違いで判定する。エラーはerrorインターフェースと、エラー種類インターフェースを実装した型として定義。