error interfészGo-ban a hibakezelés nem kivételekkel történik.
A hibák ehelyett olyan típusok hétköznapi értékei, amelyek megvalósítják a beépített error interfészt.
Az error interfész nagyon minimális.
Egyetlen metódusa van, az Error(), amely stringként adja vissza a hibaüzenetet.
type error interface {
Error() string
}
Amikor olyan függvényt definiálsz, amelynek futása közben olyan hiba történhet, amelynek el kell jutnia a hívóhoz, a visszatérési típusok közé fel kell venned az error-t.
Ha a függvénynek több visszatérési értéke van, a konvenció szerint az error mindig az utolsó.
func DoSomething() (int, error) {
// ...
}
Nem kell mindig neked megvalósítanod az error interfészt.
Egyszerű hiba létrehozásához használhatod az errors.New() függvényt, amely a szabványos könyvtár errors csomagjának része.
Egyetlen dolgot kell átadnod: a hibaüzenetet stringként, és az errors.New() gondoskodik róla, hogy létrejöjjön egy érték, amely tartalmazza az üzenetedet, és megvalósítja az error interfészt.
Ha a függvény hibát ad vissza, jó gyakorlat, ha minden más visszatérési értékre a zéróértéket adod vissza:
func DoSomething() (SomeStruct, int, error) {
// ...
return SomeStruct{}, 0, errors.New("failed to calculate result")
}
Ne feltételezd, hogy minden függvény zéróértéket ad vissza a többi visszatérési értékre, ha hiba történt. A legjobb, ha abból indulsz ki, hogy hiba esetén egyik másik visszatérési érték használata sem biztonságos. Kivételt csak azok az esetek jelentenek, amikor a dokumentáció egyértelműen kimondja, hogy a többi visszatérési értéknek hiba esetén is van jelentése.
Ha egy ilyen egyszerű hibát több helyen is szeretnél használni, érdemes külön változót deklarálni neki, ahelyett hogy minden helyen az errors.New-t hívnád.
A konvenció szerint a változó neve Err-rel vagy err-el kezdődjön (attól függően, hogy exportált-e vagy sem).
Az ilyen hibaváltozókat gyakran őrszem hibáknak nevezik.
import "errors"
var ErrNotFound = errors.New("resource was not found")
func DoSomething() error {
// ...
return ErrNotFound
}
A hibára adj vissza nil-t, ezzel jelezve, hogy a függvény futása során nem történt hiba:
func Foo() (int, error) {
return 10, nil
}
Ha olyan függvényt hívsz meg, amely hibát ad vissza, gyakori, hogy a hiba értékét egy err nevű változóban tárolod.
Mielőtt felhasználod a függvény tényleges eredményét, ellenőrizned kell, hogy nem történt-e hiba.
Hogy elkerüld a kódod „happy path” részének egymásba ágyazását, először a hibaeseteket kell kezelned.
A == és a != operátorral összehasonlíthatjuk a hibát a nil-lel, és tudjuk, hogy hiba történt, ha az err nem nil.
func processUserFile() error {
file, err := os.Open("./users.csv")
if err != nil {
return err
}
// do something with file
}
A legtöbbször a hiba a hívási láncon felfelé visszaadódik, ahogy a fenti példában is látszik. A hibát másképp is kezelheted: naplózhatod, és folytathatod valami mással. Jó gyakorlat, ha a hibát vagy visszaadod, vagy naplózod, de soha nem mindkettőt.
Mivel a Go-ban a legtöbb függvény visszatérési értékei között szerepel egy hiba, az if err != nil mintával lépten-nyomon találkozni fogsz a Go-kódban.
Ha a hibádban a hibaüzenet stringnél több információt szeretnél elhelyezni, létrehozhatsz egyedi hibatípust.
Ahogy korábban említettük, a Go-ban minden, ami megvalósítja az error interfészt (vagyis rendelkezik egy Error() string metódussal), használható hibaként.
Az egyedi hibatípus létrehozására általában egy structot használunk.
A konvenció szerint az egyedi hibatípusok neve Error-ral végződjön.
Az is a legjobb, ha az Error() string metódust pointer átvevővel definiálod; a magyarázatért nézd meg ezt a Stackoverflow-hozzászólást.
Ez azt jelenti, hogy pointert kell visszaadnod az egyedi hibádra, különben nem fog error-nak számítani, mert a nem pointer érték nem biztosítja az Error() string metódust.
type MyCustomError struct {
message string
details string
}
func (e *MyCustomError) Error() string {
return fmt.Sprintf("%s, details: %s", e.message, e.details)
}
func someFunction() error {
// ...
return &MyCustomError{
message: "...",
details: "...",
}
}
Eljött végre a nap, amire olyan sokáig vártál, és most büszke tulajdonosa vagy egy gyönyörű farmnak az Alpokban.
Továbbra sem szeretsz túl korán kelni, hogy megetesd a teheneidet.
Mivel kiváló mérnök vagy, építesz egy etetőadagolót, a FEED-M-ALL-t.
A projekted befejezéséhez már csak egyetlen dolog hiányzik: egy kódrészlet, amely kiszámítja, mennyi takarmányt kapjon minden tehén. Fontos, hogy minden tehén ugyanannyit kapjon, el kell kerülnöd a konfliktusokat. A tehenek nagyon érzékenyek.
Szerencsére nem neked kell kidolgoznod a takarmánymennyiség kiszámításához szükséges összes képletet. Egy titokzatos külső könyvtárat használsz, amelyet az interneten találtál. Elvileg ettől lesznek a legboldogabbak a tehenek. A könyvtár közzétesz egy típust, amely megvalósítja a következő interfészt. Erre fogsz támaszkodni a saját kódodban.
type FodderCalculator interface {
FodderAmount(int) (float64, error)
FatteningFactor() (float64, error)
}
Ahogy a kódodon dolgozol, fokozatosan javítani fogod a hibakezelést, hogy az robusztusabb legyen, és később könnyebben lehessen debugolni, amikor a mindennapi farméletedben használod.
Először is arra összpontosítasz, hogy megírd a tehenenkénti takarmánymennyiség kiszámításához szükséges kódot.
Valósítsd meg a DivideFood függvényt, amely argumentumként egy FodderCalculator-t és a tehenek számát egész számként fogadja.
Ennél a részfeladatnál feltételezed, hogy az átadott tehenek száma mindig nagyobb nullánál.
A függvénynek a tehenenkénti takarmánymennyiséget kell visszaadnia float64-ként, vagy egy hibát, ha az történt.
A számításhoz először le kell kérdezned az összes tehén teljes takarmánymennyiségét.
Ezt úgy teheted meg, hogy meghívod a FodderAmount metódust, és átadod neki a tehenek számát.
Ezenkívül szükséged van egy tényezőre is, amellyel ezt a mennyiséget meg kell szorozni.
Ezt a tényezőt a FatteningFactor metódus meghívásával kapod meg.
Ezzel a két értékkel és a tehenek számával most már kiszámíthatod a tehenenkénti takarmánymennyiséget (float64-ként).
Ezt kell visszaadnia a DivideFood függvénynek.
Ha a meghívott metódusok egyike hibát ad vissza, a végrehajtásnak le kell állnia, és azt a hibát változatlan formában vissza kell adnia a DivideFood függvénynek.
// For this example, we assume FodderAmount returns 50
// and FatteningFactor returns 1.5.
DivideFood(fodderCalculator, 5)
// => 15 <nil>
// Now assuming FodderAmount returns an error with message "something went wrong".
DivideFood(fodderCalculator, 5)
// => 0 "something went wrong"
Miközben az első részfeladaton dolgoztál, rájöttél, hogy a használt külső könyvtár nem olyan jó minőségű, mint gondoltad. Például nem tud megfelelően kezelni az érvénytelen bemeneteket. Ezt a korlátot úgy szeretnéd megkerülni, hogy a saját kódodban ellenőrzést adsz a bemeneti értékhez.
Írj egy ValidateInputAndDivideFood függvényt, amelynek ugyanaz a szignatúrája, mint a fenti DivideFood-nak.
DivideFood-ot, és adja vissza a hívás eredményét."invalid number of cows".ValidateInputAndDivideFood(fodderCalculator, 5)
// => 15 <nil>
ValidateInputAndDivideFood(fodderCalculator, -2)
// => 0 "invalid number of cows"
Jó döntés volt ellenőrizni a tehenek számát, mielőtt továbbadtad, de nem vagy egészen elégedett a nem túl konkrét hibaüzenettel.
Úgy döntesz, hogy jobban csinálod: létrehozol egy egyedi hibatípust InvalidCowsError néven.
Az egyedi hibának tartalmaznia kell a tehenek számát (int) és egy egyedi üzenetet (string), az Error metódusnak pedig a következő formátumban kell sorosítania az adatokat:
{number of cows} cows are invalid: {custom message}
Felvértezve az egyedi hibáddal valósítsd meg a ValidateNumberOfCows függvényt, amely a tehenek számát egész számként fogadja, és egy hibát (vagy nil-t) ad vissza.
InvalidCowsError hibát ad vissza, amelynek egyedi üzenete "there are no negative cows".InvalidCowsError hibát ad vissza, amelynek egyedi üzenete "no cows don't need food".nil-t ad vissza, jelezve, hogy az ellenőrzés sikeres volt.err := ValidateNumberOfCows(-5)
err.Error()
// => "-5 cows are invalid: there are no negative cows"
Miután rengeteg munkát fektettél ebbe az ellenőrző függvénybe, észreveszed, hogy már este van, és otthagyod az asztalodat, hogy élvezd a naplementét a hegyek fölött. Az új ellenőrző függvény tényleges beépítését a kódodba egy másik napra hagyod.
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Go nyelvet 34 fogalom166 feladat segítségével, valódi emberi mentorálással, mindez ingyen.