feature image

2021年9月12日 | ブログ記事

GolangのErrorについて分かったこと

プログラミング難しすぎんか?

こんにちは。21Bのいろりです。夏のブログリレー31日目になるはずだった記事です。

ワクチンの副作用や、書いている自分が記事についてわからなくなったりと、色々あって公開が5日ほど遅れてしまいました。反省しています...

前回は趣味の話を書きました。今回はsysAd班に関することを書こうと思います。

私がtraPに入る前のプログラミング経験はJavaScript  (正確にはGAS)  とpythonを触ったことがある程度で、RubyやPHPといったWebアプリ制作に使われる言語は完全に未経験です。よって、嘘や適切でない説明が含まれるかもしれませんが、大目に見ていただけると助かります。

今回の記事のテーマ

私はA Tour of Goを一通りざっとこなし、とりあえず課題に触ってみるというスタイルで勉強をしました。

今の時代、大抵のことはググったら出てくるわけで、困ったらとりあえずググって説明やコードを読みます。そうするとA Tour of Goで見なかった謎の文法が出てくるわけですね。

そう、これです

func 関数名(引数 引数の型)error{
	//処理
    return 関数
}

実は基本文法の応用に過ぎないのですが、Webアプリ開発の経験がないとかなり詰まる箇所だと思います。今回の記事のテーマは、これを攻略することです。

私が所属しているプロジェクト、knoQのコードで例を挙げると下のような感じです。

func (h *Handlers) HandleGetRoom(c echo.Context) error {
	roomID, err := getPathRoomID(c)
	if err != nil {
		return notFound(err)
	}

	room, err := h.Repo.GetRoom(roomID)
	if err != nil {
		return judgeErrorResponse(err)
	}
	return c.JSON(http.StatusOK, presentation.ConvdomainRoomToRoomRes(*room))
}

え、ちょっと待て `error` てなに? 

初心者がサンプルコードを読む際は、未知の関数が頻出するわけです。

ただでさえ呼び出されている関数の意味がわからないのに、全体の大枠すら掴めないとなると地獄を見ます。そしてこの文法はかなり頻出です。

結論から言えば 「errorは型に過ぎない」 のですが、この一言で分かるようならばこんな記事を読んでいないと思うので  (そして私もわからんかったので)  、詳しく解説していきます。

関数の書き方のおさらい

Goにおいて関数は以下のように書けます

func 関数名(引数 引数の型){
	//処理
}

そして戻り値がある場合は以下のように書けます

func 関数名(引数 引数の型) 戻り値の型{
	//処理
    return 戻り値
}

例) A Tour of Goに載っている例を使うと以下の通り

func add(x, y int) int {
	return x + y
}

本題には関係がありませんが、以下のようにも書けます

func 関数名(引数 引数の型)(戻り値 戻り値の型){
	//処理
	return 
}

例) 同様に

func add(x, y int) (sum int) {
	sum = x + y
    return
}

Goにおけるエラーハンドリング

Webアプリケーションでエラーが発生した際、早急に問題を解決するため、何がエラーの原因かを特定する必要があります  (リクエストがおかしいとか、サーバーが落ちてるとか)  。

そのためにどこの処理でエラーを起こしたのかを返す必要があります。

さて、エラー処理には言語言語で様々な方法がありますが、Goではシンプルさを追求するために特殊なエラー処理を用意せずに、多値返しが採用されました  (はぇ〜)  

つまり、Goにおける関数では、一般的な戻り値に加えて、処理に成功したかどうか  (エラーが発生していないか)  を返すエラー型の値が返されます

イメージ的には下のような感じです

他の言語でこのような関数の時,
x(引数) -> 処理 -> y(戻り値)

Goでは下のようになる
x -> 処理 -> y , err(処理の成否)

つまり

func 関数名(引数 引数の型)error{
	//処理
    return 関数
}

func 関数名(引数 引数の型)error{
	//処理
    err := 関数
    return err
}

と同義であり、この内側の関数の戻り値のerr  (そしてこれはerror型である)  をreturnするという意味だとわかります。

また、成功した場合  (エラーが発生しなかった場合)  はnilが返されます

if err != nil とは

一つの関数の中で、複数の関数を呼び出すことは頻繁にあります。そしてある関数でエラーが発生した際は、残りの処理を中断してエラーが起きたこととエラーの理由を返す必要があります  (エラー処理)  。

これを

err := 関数
if err !=nil{
	//errorを返却する処理
}

のように書きます

また

if err := 関数;err !=nil{
	//errorを返却する処理
}

のように、関数とそのエラー処理をまとめて書くこともあります。

これはエラー処理に限った話ではなく、Goでは2行の処理を間に ;を挟むことで一行にまとめることができます。

以上のことを踏まえると、上であげたknoQのコード

func (h *Handlers) HandleGetRoom(c echo.Context) error {
	roomID, err := getPathRoomID(c)
	if err != nil {
		return notFound(err)
	}

	room, err := h.Repo.GetRoom(roomID)
	if err != nil {
		return judgeErrorResponse(err)
	}
	return c.JSON(http.StatusOK, presentation.ConvdomainRoomToRoomRes(*room))
}

を読んだ際、この関数の大枠について以下のようなことが推測できます

JavaScriptで言えば try...catch に近い処理なのですが、Goではこのように書いていくわけですね〜

_ とは

_ とは値を指定しないことです。

_ には多様な用途がありますが  (for文で繰り返す回数を _にすると無限ループができたり)  、エラーに関しては明らかにエラーが起きないと予測される関数に対して使われることがあります。

つまり

jst, err := time.LoadLocation("Asia/Tokyo")
if err !=nil{
	//errorを返却する処理
}

jst, _ := time.LoadLocation("Asia/Tokyo")

のようにエラーを返す第二引数を_ にしてのエラー処理を省略しすることができます。

Goでは、定義した文字を使わないとエラーが出る仕様になっているため、使わない場合は_ にしておく必要があるんですね~  (n敗)

参考記事

より詳しく知りたい方は、以下の記事がかなり分かりやすいと思います。この記事を書く際も一部参考にさせていただきました。

Goエラーハンドリング戦略

終わりに

SysAd班に入りプロジェクトに所属とすると、必要性から半強制的にWebアプリ開発の知識が身につきます。特に経験者に気軽に質問できる環境  (discordでの進捗会とかsodanチャンネルとか)  は貴重です。

つよつよWebエンジニアになりたい人は、traP sysAd班に入り、プロジェクトに所属してみるのはどうでしょうか?

私を育ててくれたknoQの先輩方、ハッカソンや講習会で丁寧にWebエンジニアとしての基礎を教えてくださった先輩方への感謝を述べて、この記事を終わりたいと思います。

32日目の記事はkotoki_bisさんです。

irori icon
この記事を書いた人
irori

グラフィック班とsysAd班(とサウンド班)で活動しています

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2021年8月12日
CPCTFを支えたWebshell
mazrean icon mazrean
2021年5月19日
CPCTF2021を実現させたスコアサーバー
xxpoxx icon xxpoxx
2021年5月16日
CPCTFを支えたインフラ
mazrean icon mazrean
2021年4月2日
traQの検索機能が謎のエラーを吐いた話
toki icon toki
2021年9月8日
五度圏⊃自然音階って…コト!?
kotoki_bis icon kotoki_bis
2021年9月3日
部活青春系エロゲで涙腺崩壊した話
mera icon mera
記事一覧 タグ一覧 Google アナリティクスについて