Nilは常にnilとは限りません

Nilは常にnilとは限りません


「なに?ここに何が書かれているの?」 お願いします。 今から全部出します。


私が言語を学び始めたとき、私はこの狭いケースに来るとは思いませんでした。 また、反復可能なコレクションを変更することは合理的ではありません。


例:

func Foo() error { var err *os.PathError = nil return err } func main() { err := Foo() fmt.Println(err) // <nil> fmt.Println(err == nil) // false } 

ワット!


インターフェイスとは何ですか?

go runtime / runtime2.goパッケージファイルに移動して、以下を参照してください。


 type itab struct { // 40 bytes on a 64bit arch inter *interfacetype _type *_type ... } 

インターフェイスには、インターフェイスのタイプと値のタイプ自体が格納されます。


AND値と型がnilの場合、エラーだけでなく、インターフェイスの値はnilです。


Foo関数は、* os.PathError型のnilを返します。結果を、nil型のnilと比較します。


おそらく多くの人がこれを知っていたかもしれませんが、実際にそれに入る方法を考える人はほとんどいません。


私の例

 type Response struct { Result ResponseResult `json:"result,omitempty"` Error *ResponseError `json:"error,omitempty"` } type ResponseError struct { Message string `json:"message"` } func (e *ResponseError) Error() string { return e.Message } ... func (s *NotificationService) NotifyIfError(w *ResponseWriter) error { ... var res handlers.Response _ = json.Unmarshal(body, &res) if res.Error == nil { return } return s.NotifyError(err) } 

応答には常に結果またはエラーがあります。


エラーがある場合、必要に応じて通知サービスを通じて送信します。
サービス内でErrorメソッドが呼び出され、値がnilであるため、パニックが発生します。


どうする

厳密にインターフェイスタイプのインターフェイスを返します。


エラーの場合-エラーのタイプ。



 func (s *NotificationService) NotifyIfError(w *ResponseWriter) error { ... var res Response _ = json.Unmarshal(body, &res) var err error = res.Error return s.NotifyError(err) } 

驚いたことに、この手法も機能しません。


err変数に値を割り当てるときに、nilではない型に関する初期情報もそれに渡すことがわかります。



 func (s *NotificationService) NotifyIfError(w *ResponseWriter) error { ... if e, ok := err.(*ResponseError); ok && e == nil { return s.NotifyError(err) } return nil } 

はい、この手法は機能します。


しかし、正直に言うと、送信するすべての種類のエラーをチェックする余裕はありません。


データベースドライバーからのすべてのエラー、すべての内部エラー、その他のごみです。


私が見る最も合理的なオプションは何ですか:

 func (s *NotificationService) NotifyIfError(w *ResponseWriter) error { var err error ... var res Response _ = json.Unmarshal(body, &res) if res.Error != nil { return s.NotifyError(err) } return nil } 

最初に、値がnilであることが判明したため、エラー型の変数を宣言しました。
そして、この変数に型と値を渡す前に、nilで型とその値を確認しましょう。


これにより、パニックに陥ることがなくなります。


最後に

さらに進んで、次のようにResponse、OptionalErrorまたはErrorOrNilタイプの「オプション」エラーを実装できます。


 func (r *Response) ErrorOrNil() error { if r.Error == nil { return nil } return r.Error } 

ご注意

Go wikiノートでは、レビューコードはインターフェイスに関するトピックのノートです。代わりに、具体的な型を返し、コンシューマにプロデューサ実装のモックを作成します。


上記のダンスはこれに関するものではないことに注意してください。


私のメモでは、興味を戻したいことがわかっているときにパニックに陥ることがなく、エラーが発生した場合は常にインターフェイスを返したいと考えています。


ただし、特定の型を返す余裕がある場合は、それを返します。


参照資料

内臓


私は

LinkedIn
電報
Twitter
Github



Source: https://habr.com/ru/post/J449714/


All Articles