strconv.Atoiのerrを無視するとゼロ値のまま処理が進む
strconv.Atoiは変換に失敗するとerrを返しますが、これを無視すると1番目の戻り値は黙って0のままになります。エラーチェックをしないと、変換失敗が気づかれずに処理が進んでしまいます。
なぜエラーが出ないのか
出力: 0
(エラーなし)- Goは何も報告しません。コンパイルも実行も正常に終わるコードだからです
出力: 0- 実際の出力 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 package main 2 3 import ( 4 "fmt" 5 "strconv" 6 ) 7 8 func main() { 9 n, err := strconv.Atoi("abc") 10 if err != nil { 11 ^ 12 } 13 fmt.Println(n * 2) 14 }
errを無視すると変換失敗に気づけず、nはゼロ値の0のまま計算に使われてしまいます。失敗時はわかりやすい値に倒すべきです。
直し方: (空) を n = -1 にします。
パターン2
1 package main 2 3 import ( 4 "fmt" 5 "strconv" 6 ) 7 8 func main() { 9 n, err := strconv.Atoi("xyz") 10 if err != nil { 11 ^ 12 } 13 fmt.Println(n * 3) 14 }
errチェックの中身が空だと、変換に失敗したことに誰も気づかないまま処理が続きます。
直し方: (空) を n = -1 にします。
パターン3
1 package main 2 3 import ( 4 "fmt" 5 "strconv" 6 ) 7 8 func main() { 9 n, err := strconv.Atoi("12x") 10 if err != nil { 11 ^ 12 } 13 fmt.Println(n * 10) 14 }
数字混じりの文字列でもAtoiは全体が数値でなければ失敗し、nは0のまま返ります。
直し方: (空) を n = -1 にします。
パターン4
1 package main 2 3 import ( 4 "fmt" 5 "strconv" 6 ) 7 8 func main() { 9 n, err := strconv.Atoi("") 10 if err != nil { 11 ^ 12 } 13 fmt.Println(n * 5) 14 }
空文字列を渡しても変換に失敗しますが、エラーを見ていなければ0が返ったことにしか見えません。
直し方: (空) を n = -1 にします。
パターン5
1 package main 2 3 import ( 4 "fmt" 5 "strconv" 6 ) 7 8 func main() { 9 n, err := strconv.Atoi("one") 10 if err != nil { 11 ^ 12 } 13 fmt.Println(n * 4) 14 }
errが返ってきた場合にnを明示的なエラー値に倒しておけば、後段の処理で異常だとすぐわかります。
直し方: (空) を n = -1 にします。
よくある誤解
「変換に失敗すればpanicするかプログラムが止まるはず」という思い込みは誤りです。Goのエラーは戻り値として返されるだけで、無視すればプログラムはそのままゼロ値を使って処理を続行します。
実務での勘所
Goがtry-catchではなく、通常の戻り値としてerrorを返す設計を選んでいるのは、エラー処理を必ずコードの上に見える形で書かせたいという意図によるものです。例外機構のように、どこか離れた場所で暗黙にキャッチされる心配が無く、if err != nilという形で必ずその場に処理が残ります。ただしこの設計の弱点がまさにこのトピックの症状です。errorはただの戻り値である以上、言語として無視することを禁止する仕組みが無く、代入せずに切り捨てることも構文的には可能です。「見える化」を優先した設計が、「見ても見ぬふりができる」という抜け道と表裏一体になっています。