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
業務時間外もAIに働いてもらう話
colorful12
3
10k
AWS DevOps Agentで インシデント対応をAIに任せたい
honmarkhunt
7
2.8k
XP祭りでしか伝わらないフリップネタ #xpjug
murabayashi
0
140
ALB ログから Trace を気合で繋げる技術
fohte
7
890
Claude Codeを組織的に動かして月400PRを実現した話
happy_ryo
0
270
Swift愛好会100回記念 第1回を振り返る
jollyjoester
0
120
Can LLMs Replicate 4 Years of Compose Migration? Exploring the boundaries of automation with 279 XML files from a real product
makun
0
130
【高い買い物LT会】初任給で話題の国産フィジカルAIを買った話
akagami
PRO
0
150
AgentCore CLI で進化した AWS での AI エージェントの作り方 : 必要な機能を必要な時に
icoxfog417
PRO
3
320
GKE アップグレード前に知っておきたい Blue/Green と PDB の関係
stkk
0
160
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
280
iOS開発×AI駆動開発 〜最近使って便利だったスキルの話〜
nogu66
0
150
Featured
See All Featured
Measuring & Analyzing Core Web Vitals
bluesmoon
9
990
Music & Morning Musume
bryan
47
7.4k
Statistics for Hackers
jakevdp
799
230k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
270
Automating Front-end Workflow
addyosmani
1369
210k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3k
The Cost Of JavaScript in 2023
addyosmani
55
10k
Abbi's Birthday
coloredviolet
3
9.9k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
Building Adaptive Systems
keathley
44
3.2k
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