3 dingen die het verschil maken

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.

1. Eenvoud

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. 

Geen generica

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. 

Geen uitzonderingen

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 

Eén uitvoerbaar bestand

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.   

Geen dynamische bibliotheken

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.

2. Goroutines

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.

CSP

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.

Roep een functie aan als een goroutine

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.

Synchroniseer Goroutines

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

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  

Schrijf een goroutine

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.

3. Foutafhandeling

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 ()) 

Verplichte controle

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 ()

Taalondersteuning

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.

Interactie met Goroutines

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.

Conclusie

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.