Null ist nicht immer Null
"Was? Was steht hier geschrieben?" du fragst. Jetzt werde ich alles löschen.
Als ich anfing, die Sprache zu lernen, dachte ich nicht, dass ich zu diesem engen Fall kommen würde. Es ist auch nicht rational, eine iterierbare Sammlung zu ändern.
Zum Beispiel:
func Foo() error { var err *os.PathError = nil return err } func main() { err := Foo() fmt.Println(err) // <nil> fmt.Println(err == nil) // false }
WAT!
Was ist eine Schnittstelle?
Wechseln Sie zur Paketdatei go runtime / runtime2.go und sehen Sie:
type itab struct { // 40 bytes on a 64bit arch inter *interfacetype _type *_type ... }
Eine Schnittstelle speichert den Schnittstellentyp und den Werttyp selbst.
Der Wert einer Schnittstelle, nicht nur ein Fehler, ist Null, wenn die UND-Werte und der Typ Null sind.
Die Foo-Funktion gibt nil vom Typ * os.PathError zurück. Wir vergleichen das Ergebnis mit nil vom Typ nil, woraus sich ihre Ungleichung ergibt.
Vielleicht wussten viele davon, aber nur wenige Leute denken darüber nach, wie sie es in der Praxis schaffen können.
Mein Beispiel
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) }
Die Antwort hat immer ein Ergebnis oder einen Fehler.
Wenn ein Fehler auftritt, senden wir ihn bei Bedarf über den Benachrichtigungsdienst.
Innerhalb des Dienstes wird die Fehlermethode aufgerufen, und da unser Wert Null ist, geraten wir in Panik.
Was zu tun ist?
Geben Sie eine Schnittstelle ausschließlich eines Schnittstellentyps zurück.
Im Fehlerfall - Art des Fehlers.
- Fügen Sie eine Deklaration vom Typ Fehler hinzu
func (s *NotificationService) NotifyIfError(w *ResponseWriter) error { ... var res Response _ = json.Unmarshal(body, &res) var err error = res.Error return s.NotifyError(err) }
Zu meiner Überraschung funktioniert diese Technik auch nicht.
Es stellt sich heraus, dass wir beim Zuweisen eines Werts zur err-Variablen auch die anfänglichen Informationen über den Typ übergeben, die nicht null sind.
- Versuchen wir, unseren Quelltyp vom Schnittstellentyp abzurufen und seinen Wert zu überprüfen.
func (s *NotificationService) NotifyIfError(w *ResponseWriter) error { ... if e, ok := err.(*ResponseError); ok && e == nil { return s.NotifyError(err) } return nil }
Ja, diese Technik funktioniert.
Aber um ehrlich zu sein, können wir es uns nicht leisten, alle Arten von Fehlern zu überprüfen, die wir übertragen werden.
Es können alle Fehler des Datenbanktreibers, alle unsere internen Fehler und anderer Müll sein.
Was ist die rationalste Option, die ich sehe:
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 }
Zuerst haben wir eine Variable vom Typ error deklariert, wie sich mit dem Wert und dem Typ nil herausstellt.
Bevor wir unseren Typ und Wert an diese Variable übergeben, überprüfen wir unseren Typ und seinen Wert auf Null.
Dadurch können wir nicht in Panik geraten.
Am Ende
Sie können noch weiter gehen und einen „optionalen“ Fehler mit dem Typ Response, OptionalError oder ErrorOrNil wie folgt implementieren:
func (r *Response) ErrorOrNil() error { if r.Error == nil { return nil } return r.Error }
Hinweis
In den Go-Wiki-Notizen ist die Codeüberprüfung eine Anmerkung im Thema zur Benutzeroberfläche: Geben Sie stattdessen einen konkreten Typ zurück und lassen Sie den Verbraucher die Herstellerimplementierung verspotten.
Ich stelle fest, dass es bei den obigen Tänzen nicht darum geht.
Meine Notizen ermöglichen es Ihnen, nicht in Panik zu geraten, wenn Sie wissen, dass Sie Zinsen zurückgeben möchten, und im Fehlerfall möchten Sie immer die Schnittstelle zurückgeben.
Wenn Sie es sich jedoch leisten können, einen bestimmten Typ zurückzugeben, geben Sie ihn zurück.
Referenzen
Go-Interna
Ich bin
LinkedIn
Telegramm
Twitter
Github