質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

解決済

1回答

1544閲覧

Go「resp.Body.Close()」のresponseについて

mono_chrome18

総合スコア14

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

0クリップ

投稿2022/06/23 10:51

Golangでレスポンスを閉じる際におまじない的に下記を使ってました。

go

1defer resp.Body.Close()

しかし、Close()の戻り値を見るとerrorが設定されていたので、エラーハンドリングが必要なんじゃないかなと思ったのですが、deferで実行しているだけでerrとして受け取ってハンドリングしているようなものは調べてもでてきませんでした。
errが返ってくることはあるのでしょうか?
ある場合なぜみなエラーハンドリングしてないのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

この「一部のエラーハンドリングの無視」はGoの慣習に対し反しているのでは?という疑問はGoを学ぶ上で多くの人が通過するところです。

多くのCloseメソッドの役割は「リソースの後始末」です。「借りたものを返す」イメージです。
そしてここでエラーが返ることがあるのか?というとかなりレアなケースで存在しますが、それは「借りたものを返す」だけで済まないハイレベルな要求を満たすためだったりします。
(もちろんコードを追いかけると絶対エラーにならない実装もあります)

実際にいろんなCloseメソッドの実装を見て回ってみると良いでしょう。

http.Responseの内部で使っているcloseBodyという処理を見てみるとそもそもエラーを見ていません。
https://github.com/golang/go/blob/master/src/net/http/response.go#L336-L340

つまり、Response.Body.Closeはほとんどのケースでエラーの返らないメソッドだからエラーを無視しているのです。

レアケースな例としては「バッファ付きのio.WriteCloser」に該当するものですね。
例えばos.Createなどが返すファイル構造体などがこれに該当しますが、この*File.Closeの処理には2つの役割があります。

  • バッファに残りがあればそれを書き出す
  • その後リソースを返却する

後者はエラーが発生するときはOSのトラブル等かなり超レアな致命的な場合のみで、通常エラーになることはありません。前者はそれなりにエラーが発生することは起こりえます(書き出し先USBメモリのアンマウントなど)。

なのでファイルの書き出し系では以下のように書くことが推奨されています。

go

1fp, err := os.Create("sample.txt") 2if err != nil { /* error handling */ } 3defer fp.Close() 4if _, err := fmt.Fprintln(fp, "hello!"); err != nil { /* error handling */ } 5if _, err := fmt.Fprintln(fp, "hello!"); err != nil { /* error handling */ } 6if _, err := fmt.Fprintln(fp, "hello!"); err != nil { /* error handling */ } 7if err := fp.Sync(); err != nil { /* error handling */ }

Syncにより、バッファの内容をファイルに書ききってしまうという処理をあらかじめすましておきます。
そうすると、defer fp.Close()がエラーを返す状況はOSのトラブル等致命的な状況のみになります。

では致命的なエラーを見ないといけないのでは?

という疑問が続くのはごもっともなんですが、その状況が起きたとき、それを検出したりユーザーにお知らせする役割はアプリケーションではなく、OSの仕事です。
Goアプリケーションがその致命的なエラーを検出したとしても、その後それをログに残したり、コンソールに出力できるのかというとそれも期待できないような状況が「致命的な」状態なのです。

投稿2022/06/23 13:53

nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobonobo

2022/06/23 13:56

Goで「自作ライブラリでこの手のメソッドを実装するとき、利用者にはメジャーケースでエラーを返さないことが期待されている」ということでもあります。
mono_chrome18

2022/06/27 04:52

大変丁寧に回答いただきありがとうございました。 疑問点から例に至るまでここまで詳細に書いていただけて感動しました。 Goはエラーハンドリングするのが基本だと考えていたため、まさにそこを疑問視していました。 OSレベルでの致命的なエラーでしか起こり得ないようなモノに関しては、アプリケーション側のエラーハンドリングに期待しないという考えなんですね。 > Goで「自作ライブラリでこの手のメソッドを実装するとき、利用者にはメジャーケースでエラーを返さないことが期待されている」ということでもあります。 こちらの点だけ、私が上手く汲み取れず、、 この手のメソッドとはどのようなものでしょうか。 メジャーケースでエラーを返さないというのは、メソッドを実装する際にresponseにerrを含めないということでしょうか。
nobonobo

2022/06/27 08:50

自作のオブジェクトに例として「Closeメソッド」をはやす時、errorを返す定義なんだけども利用者にerrorの対処が必要な設計をしないということです。
mono_chrome18

2022/06/28 05:21

なるほど!理解できました! 本当にありがとうございました! もっと学んでいきたいと思います!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問