Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Go でのエラー生成パターン
Search
KoharaKazuya
July 26, 2019
Programming
17k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Go でのエラー生成パターン
Umeda.go 2019 Summer
KoharaKazuya
July 26, 2019
More Decks by KoharaKazuya
See All by KoharaKazuya
Native File System API の紹介と Zip を作る Web サービスを作った話
koharakazuya
0
200
グラフィカルに URL を編集できるサービスを作った
koharakazuya
1
16k
pickpatch を作った話と仕組みの解説
koharakazuya
0
62
Other Decks in Programming
See All in Programming
プロダクトコードからライブラリの境界を見つける
elmetal
PRO
0
110
速く作れる。その次は、速く確かめられる開発へ 〜AIネイティブ開発を支える、Shift Down〜 / Can build fast. Next, moving to development where we can verify fast.
rkaga
8
5.6k
コードレビューのボトルネックを"する側"と"される側"の両面から解消する
yub0n
2
1.5k
AIに “答え“を書かせるのを やめてみた
___akari11
0
110
SalesForceを内製化!? ~ HR事業を支える顧客管理基盤のインフラを大公開 ~
oku053
0
150
Jetpack Compose Mechanisms
skydoves
2
550
パーソナライズPodcastを作った話
repunit11
0
130
When benchmarks go bad - what I learned from measuring performance wrong
hollycummins
0
180
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
450
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
350
Turning Architecture into Unit Tests in the AI Era (NSSpain XIV)
steliosf
PRO
1
130
更なる可用性を求めて、5年間運用したKotlinのアプリケーションをGoでリプレイスする話
ken_tunc
0
450
Featured
See All Featured
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
840
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
970
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
290
How to Think Like a Performance Engineer
csswizardry
28
2.9k
From π to Pie charts
rasagy
1
390
Designing Powerful Visuals for Engaging Learning
tmiket
1
600
Statistics for Hackers
jakevdp
799
230k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
68
58k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.8k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
2
2.9k
Designing for humans not robots
tammielis
254
26k
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
860
Transcript
Go でのエラー⽣成パターン Umeda.go 2019 Summer
今⽇話すこと 「Go でどんな error を⽣成するか」 のいくつかのパターンを紹介 & ⽐較する 2 2
⾃⼰紹介 ⼩原 ⼀哉 (こはら かず や) ウェブエンジニア フェンリル株式会社 @KoharaKazuya 3
3
背景 「Go がそこそこ書けるようになってきたぞ。 よし、エラー処理を書こう」 「あれ、エラーの書き⽅は⾊々あるっぽい」 「⾊々あるけどどう違うんだろう? どれがいいんだろう?」 4 4
まずは⾃分の求めているもの明確にする 5 5
私が「エラー」に求めるもの 1. わかりやすいメッセージを出⼒できること エラー発⽣時にすぐに原因を理解するため 2. プログラムがエラーの種類を識別できること 回復や変換などのエラーハンドリングのため 3. スタックトレースを持つこと エラー発⽣時に発⽣した箇所を知るため
6 6
私が「エラー」に求めるもの 4. エラーの原因のエラーを辿れること エラーの原因のエラーが存在するなら そちらの解決が必要な場合がある 5. エラー種別ごとに追加の情報を持てること エラーによっては追加の情報を プログラムに読ませたい場合がある 例:
HTTP Retry-After ヘッダー 7 7
開発者が 読めることと プログラムが 読めることを 混ぜて考えない ↓ エラーの種類の識別はプログラムのため メッセージは開発者のため スタックトレースの表⽰は開発者のため エラーの原因を辿るのは開発者のため
※ 1 ※1 理想的にはプログラムは直接呼び出す API が定義するエラーのみ相⼿にすれば良い。 エラーを⽣成する側は原因となったエラーを「⾃分の⾔葉に翻訳して」エラーを ⽣成し直すべき。エラーの原因を辿るのは知るためで挙動を変えるためではないはず。 8 8
これらの観点で様々なエラーの⽅法を ⽐較していく 9 9
メッセージ⽂字列チェック エラーメッセージの⽂字列を使って種別を判定する⽅法 → やるべきではない 開発者向けのメッセージをプログラムに読ませるべきではない 10 10
No Handling if err != nil { return err }
→ やるべきではない 利⽤者にとって知らないエラーが出るので基本は避けるべき。 例: 「両替」ロジックの API を使ったら `permission denied` というエラーが出てきた 11 11
翻訳 if err != nil { return fmt.Errorf("database error: %v",
err) } 下位のエラーを翻訳して上位に投げるだけ 12 12
翻訳 観点 可否 理由 1. メッセージ ✔ error はメッセージを持つ 2.
種類の識別 - その場限りの値のため 3. スタックトレース - fmt.Errorf により 4. エラーの原因 ✔ %v に⼊る 5. 追加情報 - ⽂字列のみ 13 13
Sentinel Errors // 定義 var ( ErrInvalid = errors.New("invalid argument")
ErrPermission = errors.New("permission denied") ) // ⽣成 return ErrInvalid // ⽐較 if err == os.ErrInvalid { // ... } 14 14
Sentinel Errors 観点 可否 理由 1. メッセージ ✔ error はメッセージを持つ
2. 種類の識別 ✔ 等価性により確認可能 3. スタックトレース - 固定の値のため 4. エラーの原因 - 固定の値のため 5. 追加情報 - 固定の値のため 15 15
専⽤の型 // 定義 type PathError struct { // ... }
// ⽣成 return &PathError{ ... } // ⽐較 if x, ok := err.(*os.PathError); ok { // ... } 16 16
専⽤の型 観点 可否 理由 1. メッセージ ✔ error はメッセージを持つ 2.
種類の識別 ✔ 型アサーションの成否により 3. スタックトレース ※ 値を⽣成時に埋め込み 4. エラーの原因 ※ 値を⽣成時にラップ 5. 追加情報 ✔ 型ごとに独⾃定義 ※ error.Error のメッセージに混ぜ込めるがノイジー 17 17
特殊なチェック関数 // 定義 func IsNotExist(err error) bool { return err
== syscall.ENOENT } // もしくは func IsNotExist(err error) bool { if x, ok := err.(*customError); ok { return x.code == 123 } return false } // ⽐較 if os.IsNotExist(err) { // ... } 18 18
特殊なチェック関数 観点 可否 理由 1. メッセージ ✔ error はメッセージを持つ 2.
種類の識別 ✔ 型アサーションの成否により 3. スタックトレース N/A エラー⽣成⽅法は任意 4. エラーの原因 N/A エラー⽣成⽅法は任意 5. 追加情報 N/A エラー⽣成⽅法は任意 19 19
ここまでは昔の知識 20 20
最近のエラー事情 Go では⻑らく エラーとはエラーメッセージ⽂字列を持っている値だ というスタンスを取ってきたが、 エラーの ラップ と 詳細情報の出⼒ の標準化のため
Go 1.13 からオプショナルなインターフェースと 標準機能が追加されることになった。 (現在でも xerrors パッケージで同等の機能が利⽤可能) 21 21
エラーがラップ可能になった ( %+v でスタックトレースも) 22 22
種類の識別をラップ対応版にする Sentinel Errors // 等価性テスト if err == os.ErrInvalid {
... } // ラップ対応版 if errors.Is(err, os.ErrInvalid) { ... } 23 23
種類の識別をラップ対応版にする 専⽤の型 // 型アサーションの成否 if x, ok := err.(*os.PathError); ok
{ ... } // ラップ対応版 var x *os.PathError if errors.As(err, &x) { ... } 24 24
各種インターフェースを実装した型 観点 可否 理由 1. メッセージ ✔ error はメッセージを持つ 2.
種類の識別 ✔ 型アサーションの成否により 3. スタックトレース ✔ 値を⽣成時に埋め込み 4. エラーの原因 ✔ 値を⽣成時にラップ 5. 追加情報 ✔ 型ごとに独⾃定義 適切にインタフェースを実装すれば全て可能 ただし、 エラー種別ごとに実装が必要なので⾯倒 25 25
各種インターフェースを実装した型 エラー種類ごとに実装がいちいち実装が必要で ⾯倒くさい Sentinel Errors スタックトレース、エラーの原因、追加情報などを持てず エラー情報として 貧弱 26 26
⼿軽でリッチなエラーを⽣成する⽅法を考える 27 27
翻訳 if err != nil { return fmt.Errorf("database error: %v",
err) } 下位のエラーを翻訳して上位に投げるだけ → 実は何もしなくても Go 1.13 でアップデートされる 28 28
翻訳 観点 可否 理由 1. メッセージ ✔ error はメッセージを持つ 2.
種類の識別 - その場限りの値のため 3. スタックトレース ✔ fmt.Errorf により 4. エラーの原因 ✔ %v に⼊る 5. 追加情報 - ⽂字列のみ 29 29
Sentinel Errors + fmt.Errorf // 定義 var ( ErrInvalid =
errors.New("invalid argument") ErrPermission = errors.New("permission denied") ) // ⽣成 return fmt.Errorf("error: %w", ErrInvalid) ラップしつつ新しくエラーを⽣成するため、 スタックトレースが出⼒できる 追加のメッセージが少し冗⻑ 30 30
Sentinel Errors + fmt.Errorf 観点 可否 理由 1. メッセージ ✔
error はメッセージを持つ 2. 種類の識別 ✔ errors.Is により 3. スタックトレース ✔ fmt.Errorf により 4. エラーの原因 - 複数のエラーのラップは不可 5. 追加情報 - ⽂字列のみ 31 31
もっとリッチなエラーを⼿軽に作りたい 32 32
ライブラリを作りました github.com/KoharaKazuya/errval github.com/KoharaKazuya/errbase 33 33
errval // 定義 var ( ErrInvalid = errval.Type("invalid argument") ErrPermission
= errval.Type("permission denied") ) // ⽣成 return ErrInvalid.New() return ErrInvalid.Wrap(err) // ラップする場合 // ⽐較 if xerrors.Is(err, provider.ErrInvalid) { // ... } 34 34
errval 観点 可否 理由 1. メッセージ ✔ error はメッセージを持つ 2.
種類の識別 ✔ errors.Is により 3. スタックトレース ✔ errval により 4. エラーの原因 ✔ ErrType.Wrap により 5. 追加情報 - errval が提供しない 35 35
errbase // 定義 type MyCustomError struct { errbase.Err CustomField string
} // ⽣成 err := &MyCustomError{ CustomField: "additional information", } err.Build("failed to xxx") return err // ⽐較 var x *MyCustomError if xerrors.As(err, &x) { } 36 36
errbase 観点 可否 理由 1. メッセージ ✔ error はメッセージを持つ 2.
種類の識別 ✔ errors.As により 3. スタックトレース ✔ errbase により 4. エラーの原因 ✔ Err.Wrap により 5. 追加情報 ✔ 独⾃の型定義が可能 37 37
⼿軽でリッチなエラー! 38 38
まとめ エラーに求める機能を整理した Go で error を⽣成する様々なパターンを紹介、⽐較した Go でエラーの⽣成をサポートするライブラリを作った github.com/KoharaKazuya/errval github.com/KoharaKazuya/errbase
39 39