Nihil tidak selalu nol
"Apa? Apa yang tertulis di sini?" kamu bertanya. Sekarang saya akan mengeluarkan semuanya.
Ketika saya mulai belajar bahasa, saya tidak berpikir bahwa saya akan sampai pada kasus sempit ini. Juga tidak rasional untuk memodifikasi koleksi yang dapat diubah.
Sebagai contoh:
func Foo() error { var err *os.PathError = nil return err } func main() { err := Foo() fmt.Println(err) // <nil> fmt.Println(err == nil) // false }
WAT!
Apa itu antarmuka?
Buka file paket go runtime / runtime2.go dan lihat:
type itab struct { // 40 bytes on a 64bit arch inter *interfacetype _type *_type ... }
Antarmuka menyimpan jenis antarmuka dan jenis nilai itu sendiri.
Nilai antarmuka apa pun, bukan hanya kesalahan, adalah nihil dalam kasus ketika nilai DAN jenisnya nihil.
Fungsi Foo mengembalikan nih dari tipe * os.PathError, kami membandingkan hasilnya dengan nihil dari tipe nil, dari mana ketidaksetaraan mereka mengikuti.
Mungkin banyak yang tahu tentang ini, tetapi hanya sedikit orang yang berpikir bagaimana melakukannya.
Contoh saya
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) }
Respons selalu memiliki hasil atau kesalahan.
Jika ada kesalahan, kami kirimkan jika perlu melalui layanan pemberitahuan.
Di dalam layanan, metode Kesalahan dipanggil, dan karena nilai kami nol, kami menjadi panik.
Apa yang harus dilakukan
Mengembalikan antarmuka secara ketat dari jenis antarmuka.
Dalam hal terjadi kesalahan - jenis kesalahan.
- Tambahkan deklarasi kesalahan jenis
func (s *NotificationService) NotifyIfError(w *ResponseWriter) error { ... var res Response _ = json.Unmarshal(body, &res) var err error = res.Error return s.NotifyError(err) }
Teknik ini, yang mengejutkan saya, juga tidak berhasil.
Ternyata ketika menetapkan nilai ke variabel err, kami juga memberikan informasi awal tentang tipe tersebut, yang bukan nihil.
- Mari kita coba untuk mendapatkan jenis sumber kami dari jenis antarmuka dan periksa nilainya.
func (s *NotificationService) NotifyIfError(w *ResponseWriter) error { ... if e, ok := err.(*ResponseError); ok && e == nil { return s.NotifyError(err) } return nil }
Ya, teknik ini berhasil.
Tapi jujur saja, kami tidak mampu memeriksa semua jenis kesalahan yang akan kami sampaikan.
Itu bisa semua kesalahan dari driver database, semua kesalahan internal kami dan sampah lainnya.
Apa pilihan paling rasional yang saya lihat:
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 }
Pertama, kami mendeklarasikan variabel tipe kesalahan, karena ternyata dengan nilai dan tipe nil.
Dan sebelum meneruskan tipe dan nilai kita ke variabel ini, mari kita periksa tipe dan nilainya pada nihil.
Ini akan memungkinkan kita untuk tidak jatuh dengan panik.
Pada akhirnya
Anda dapat melangkah lebih jauh dan menerapkan kesalahan "opsional" dengan tipe Respons, OptionalError, atau ErrorOrNil, seperti ini:
func (r *Response) ErrorOrNil() error { if r.Error == nil { return nil } return r.Error }
Catatan
Dalam catatan Go wiki, tinjauan kode adalah catatan dalam topik tentang antarmuka: Alih-alih mengembalikan jenis yang konkret dan membiarkan konsumen mengejek implementasi produsen.
Saya perhatikan bahwa tarian di atas bukan tentang ini.
Catatan saya memungkinkan Anda untuk tidak panik ketika Anda tahu bahwa Anda ingin mengembalikan minat, dan jika terjadi kesalahan, Anda selalu ingin mengembalikan antarmuka.
Tetapi jika Anda mampu mengembalikan jenis tertentu, kembalikan.
Referensi
go-internal
Saya
LinkedIn
Telegram
Twitter
Github