Go is een speciale taal. Het is heel verfrissend in zijn benadering van programmeren en de principes die het promoot. Het helpt dat sommige uitvinders van de taal vroege C-pioniers waren. Het algemene gevoel van Go is 21e-eeuws C.
In deze tutorial leert u over drie van de functies die Go uniek maken: de eenvoud, het concurrency-model via goroutines en het mechanisme voor foutafhandeling.
Veel succesvolle moderne talen zoals Scala en Rust zijn erg rijk en bieden geavanceerde type systemen en geheugenbeheersystemen. Die talen namen de standaardtalen van hun tijd, zoals C ++, Java en C # en toegevoegde of verbeterde mogelijkheden. Go nam een andere route en schakelde veel functies en mogelijkheden uit.
Generics of sjablonen vormen een pijler onder veel programmeertalen. Ze voegen vaak complexiteit toe en de foutmeldingen die worden geassocieerd met generieke geneesmiddelen kunnen soms duister zijn. Gaan ontwerpers besloten om het gewoon overslaan.
Dit is misschien wel de meest controversiële ontwerpbeslissing van Go. Persoonlijk vind ik veel waarde in generieke geneesmiddelen en geloof dat het op de juiste manier kan worden gedaan (zie C # voor een goed voorbeeld van generieke geneesmiddelen die goed zijn gedaan). Hopelijk zullen in de toekomst generieken worden opgenomen in Go.
De foutafhandeling van Golang is afhankelijk van expliciete statuscodes. Om de status van het werkelijke resultaat van een functie te scheiden, ondersteunt Go meerdere retourwaarden van een functie. Dit is vrij ongebruikelijk. Ik zal het later in meer detail bespreken, maar hier is een snel voorbeeld:
pakket main import ("fmt" "errors") func div (a, b float64) (float64, error) if b == 0 return 0, errors.New (fmt.Sprintf ("Kan% f niet delen door zero ", a)) return a / b, nihil func main () result, err: = div (8, 4) if err! = nil fmt.Println (" Oh-oh, er is iets verkeerd gegaan. " + err.Error ()) else fmt.Println (result) result, err = div (5, 0) if err! = nil fmt.Println ("Oh-oh, something iswrong." + err.Error ()) else fmt.Println (result) 2 Oh-oh, er is iets mis. Kan 5.000000 niet delen door nul
Go heeft geen aparte runtime-bibliotheek. Het genereert een enkel uitvoerbaar bestand, dat u kunt implementeren door alleen maar te kopiëren (via de XCOPY-implementatie). Dit is zo simpel als het wordt. U hoeft zich geen zorgen te maken over afhankelijkheden of niet-overeenkomende versies. Het is ook een goede manier om op containers gebaseerde implementaties uit te voeren (Docker, Kubernetes en vrienden). Het enkele zelfstandige uitvoerbare bestand maakt voor zeer eenvoudige Dockerfiles.
OK. Dit is onlangs veranderd in Go 1.8. U kunt nu dynamische bibliotheken daadwerkelijk laden via de inpluggen pakket. Maar omdat deze mogelijkheid niet werd geïntroduceerd vanaf het begin, beschouw ik het nog steeds als een uitbreiding voor speciale situaties. De geest van Go is nog steeds een statisch samengesteld uitvoerbaar bestand. Het is ook alleen beschikbaar op Linux.
Goroutines zijn waarschijnlijk het meest aantrekkelijke aspect van Go vanuit een praktisch standpunt. Met Goroutines kunt u de kracht van multicore-machines op een zeer gebruiksvriendelijke manier benutten. Het is gebaseerd op solide theoretische grondslagen, en de syntaxis om het te ondersteunen is zeer aangenaam.
De basis van Go's concurrency-model is C. A. R. Hoare's Communicating Sequential Processes. Het idee is om synchronisatie over gedeeld geheugen tussen meerdere threads van uitvoering te vermijden, die foutgevoelig en arbeidsintensief is. Communiceer in plaats daarvan via kanalen die conflicten vermijden.
Elke functie kan worden opgeroepen als een goroutine door hem te bellen via het trefwoord go. Overweeg eerst het volgende lineaire programma. De foo ()
functie slaapt gedurende enkele seconden en drukt het aantal seconden af dat het geslapen heeft. In deze versie, elke oproep naar foo ()
blokken voor het volgende gesprek.
pakket main import ("fmt" "time") func foo (d time.Duration) d * = 1000000000 time.Sleep (d) fmt.Println (d) func main () foo (3) foo (2) foo (1) foo (4)
De uitvoer volgt de volgorde van oproepen in de code:
3s 2s 1s 4s
Nu zal ik een kleine wijziging aanbrengen en het trefwoord "go" toevoegen vóór de eerste drie aanroepingen:
pakket main import ("fmt" // "errors" "time") func foo (d time.Duration) d * = 1000000000 time.Sleep (d) fmt.Println (d) func main () go foo ( 3) go foo (2) ga foo (1) foo (4)
De uitvoer is nu anders. De oproep van 1 seconde is als eerste geëindigd en de "1s" zijn afgedrukt, gevolgd door "2s" en "3s".
1s 2s 3s 4s
Merk op dat de oproep van 4 seconden geen goroutine is. Dit is in ontwerp, dus het programma wacht en laat de gorotines eindigen. Zonder dit programma wordt het programma onmiddellijk voltooid na het starten van de goroutines. Er zijn verschillende manieren naast slapen om te wachten tot een goroutine eindigt.
Een andere manier om te wachten tot goroutines zijn voltooid, is door synchronisatiegroepen te gebruiken. U declareert een wait group-object en geeft dit door aan elke goroutine, die verantwoordelijk is voor het aanroepen ervan Gedaan()
methode wanneer het klaar is. Vervolgens wacht u op de synchronisatiegroep. Hier is de code die het vorige voorbeeld aanpast om een wachtgroep te gebruiken.
pakket main import ("fmt" "sync" "time") func foo (d time.Duration, wg * sync.WaitGroup) d * = 1000000000 time.Sleep (d) fmt.Println (d) wg.Done () func main () var wg sync.WaitGroup wg.Add (3) go foo (3, & wg) go foo (2, & wg) ga foo (1, & wg) wg.Wait ()
Kanalen laten goroutines (en je hoofdprogramma) informatie uitwisselen. U kunt een kanaal maken en doorgeven aan een goroutine. De maker kan naar het kanaal schrijven en de goroutine kan het kanaal lezen.
De tegenovergestelde richting werkt ook. Go biedt ook een zoete syntaxis voor kanalen met pijlen om de informatiestroom aan te geven. Hier is nog een andere aanpassing van ons programma, waarin de goroutines een kanaal ontvangen waarop ze schrijven wanneer ze klaar zijn, en het hoofdprogramma wacht op het ontvangen van berichten van alle goroutines voordat het wordt beëindigd.
pakket main import ("fmt" "time") func foo (d time.Duration, c chan int) d * = 1000000000 time.Sleep (d) fmt.Println (d) c <- 1 func main() c := make(chan int) go foo(3, c) go foo(2, c) go foo(1, c) <- c <- c <- c
Dat is een soort truc. Het schrijven van een goroutine is hetzelfde als het schrijven van een functie. Bekijk de foo () bovenstaande functie, die in hetzelfde programma als een goroutine wordt genoemd, evenals een normale functie.
Zoals ik eerder al zei, is de foutafhandeling van Go anders. Functies kunnen meerdere waarden retourneren en bij convolutiefuncties die kunnen mislukken, wordt een foutobject geretourneerd als de laatste geretourneerde waarde.
Er is ook een mechanisme dat lijkt op uitzonderingen via de paniek()
en herstellen()
functies, maar het is het meest geschikt voor speciale situaties. Hier is een typisch scenario voor foutafhandeling waarbij de bar()
functie retourneert een fout en de hoofd()
functie controleert of er een fout was en drukt deze af.
pakket main import ("fmt" "errors") func bar () error return errors.New ("something is wrong") func main () e: = bar () if e! = nil fmt.Println ( e.Error ())
Als u de fout toewijst aan een variabele en deze niet aanvinkt, raakt Go boos.
func main () e: = bar () main.go: 15: e gedeclareerd en niet gebruikt
Er zijn manieren omheen. U kunt de fout gewoonweg niet toewijzen:
func main () bar ()
Of u kunt het toewijzen aan het onderstrepingsteken:
func main () _ = bar ()
Fouten zijn slechts waarden die u vrijelijk kunt doorgeven. Go biedt weinig foutondersteuning door de foutinterface te declareren waarvoor alleen een methode is vereist Fout()
dat geeft een string terug:
type foutinterface Fout () string
Er is ook de fouten
pakket waarmee u nieuwe foutobjecten kunt maken. De fmt
pakket biedt een Errorf ()
functie om geformatteerde foutobjecten te maken. Dat is het zo'n beetje.
U kunt geen fouten (of een ander object) van een goroutine retourneren. Goroutines kunnen via een ander medium fouten melden aan de buitenwereld. Het doorgeven van een foutkanaal aan een goroutine wordt beschouwd als een goede gewoonte. Goroutines kunnen ook fouten schrijven naar logbestanden of de database of externe services bellen.
Go heeft de afgelopen jaren enorm veel succes en vooruitgang gezien. Het is de go-to (zie wat ik daar heb gedaan) taal voor moderne gedistribueerde systemen en databases. Er zijn veel Python-ontwikkelaars geconverteerd.
Een groot deel ervan is ongetwijfeld te danken aan de steun van Google. Maar Go staat absoluut op zijn eigen merites. De benadering van het basistaalontwerp verschilt sterk van andere hedendaagse programmeertalen. Probeer het eens. Het is gemakkelijk op te pikken en leuk om in te programmeren.