3 choses qui rendent différent

Go est une langue spéciale. Il est très rafraîchissant dans son approche de la programmation et les principes qu’il promeut. Il est utile que certains des inventeurs de la langue aient été les premiers pionniers du C. Le sentiment général de Go est le 21ème siècle C. 

Dans ce didacticiel, vous découvrirez trois des caractéristiques qui rendent Go unique: sa simplicité, le modèle de simultanéité via goroutines et le mécanisme de traitement des erreurs..

1. simplicité

De nombreux langages modernes à succès, comme Scala et Rust, sont très riches et fournissent des systèmes de types avancés et des systèmes de gestion de mémoire. Ces langages ont repris les langages traditionnels de leur époque tels que C ++, Java et C # et ont ajouté ou amélioré des fonctionnalités. Go a pris un itinéraire différent et a éliminé de nombreuses fonctionnalités. 

Pas de génériques

Les génériques ou les modèles sont les piliers de nombreux langages de programmation. Ils ajoutent souvent de la complexité et les messages d'erreur associés aux génériques peuvent parfois être obscurs. Go designers ont décidé de simplement le sauter. 

C'est sans doute la décision de conception la plus controversée de Go. Personnellement, je trouve que les génériques ont une grande valeur et je pense que cela peut être fait correctement (voir C # pour un excellent exemple de génériques bien conçus). Espérons que les génériques seront intégrés à Go à l'avenir. 

Aucune exception

La gestion des erreurs de Golang repose sur des codes d'état explicites. Pour séparer le statut du résultat réel d'une fonction, Go prend en charge plusieurs valeurs de retour d'une fonction. C'est assez inhabituel. Je reviendrai plus en détail sur cela plus tard, mais voici un exemple rapide:

package main import ("fmt" "errors") func div (a, b float64) (float64, erreur) si b == 0 retour 0, errors.New (fmt.Sprintf ("Impossible de diviser% f par zero ", a)) renvoyer a / b, nil func main () résultat, erreur: = div (8, 4) si erreur! = nil fmt.Println (" Oh-oh, quelque chose s'est mal passé. " + err.Error ()) else fmt.Println (résultat) résultat, err = div (5, 0) si err! = nil fmt.Println ("Oh-oh, quelque chose ne va pas." + err.Erreur ()) else fmt.Println (résultat) 2 Oh-oh, quelque chose ne va pas. Impossible de diviser 5.000000 par zéro 

Seul exécutable

Go n'a pas de bibliothèque d'exécution séparée. Il génère un seul exécutable, que vous pouvez déployer simplement en le copiant (déploiement a.k.a.XCOPY). C'est aussi simple que cela. Pas besoin de s'inquiéter des dépendances ou des différences de version. C'est également un avantage considérable pour les déploiements basés sur des conteneurs (Docker, Kubernetes et leurs amis). Le fichier exécutable autonome permet de créer des fichiers Dockerfiles très simples..   

Pas de bibliothèques dynamiques

D'ACCORD. Cela vient de changer récemment dans Go 1.8. Vous pouvez maintenant charger des bibliothèques dynamiques via le brancher paquet. Mais, comme cette capacité n'a pas été introduite dès le départ, je la considère toujours comme une extension pour des situations spéciales. L'esprit de Go est toujours un exécutable compilé statiquement. Il est également disponible sous Linux uniquement..

2. Goroutines

Les Goroutines sont probablement l’aspect le plus attrayant de Go d’un point de vue pratique. Les Goroutines vous permettent d'exploiter la puissance des machines multicœurs de manière très conviviale. Il est basé sur des bases théoriques solides et la syntaxe pour le soutenir est très agréable.

CSP

Le modèle de concurrence de Go repose sur les processus séquentiels communicants de C. A. R. Hoare. L'idée est d'éviter la synchronisation sur la mémoire partagée entre plusieurs threads d'exécution, ce qui est sujet aux erreurs et demande beaucoup de travail. À la place, communiquez par le biais de canaux évitant les conflits.

Invoquer une fonction en tant que goroutine

Toute fonction peut être appelée en tant que goroutine en l’appelant via le mot-clé go. Considérons d’abord le programme linéaire suivant. le foo ()La fonction dort pendant plusieurs secondes et affiche le nombre de secondes de sommeil. Dans cette version, chaque appel à foo () blocs avant le prochain appel.

package principal d'importation ("fmt" "heure") func foo (d heure.Durée) d * = 1000000000 fois. Sleep (d) fmt.Println (d) func main () foo (3) foo (2) foo (1) foo (4) 

La sortie suit l'ordre des appels dans le code:

3s 2s 1s 4s

Maintenant, je vais faire une légère modification et ajouter le mot clé "go" avant les trois premières invocations:

package principal d'importation ("fmt" // "erreurs" "heure") func foo (d heure.Durée) d * = 1000000000 fois.Sommeil (d) fmt.Println (d) func principal () go foo ( 3) allez foo (2) allez foo (1) foo (4)

La sortie est différente maintenant. L'appel d'une seconde s'est terminé en premier et a imprimé "1", suivi de "2" et "3". 

1s 2s 3s 4s

Notez que l'appel de 4 secondes n'est pas une goroutine. Ceci est voulu, donc le programme attend et laisse les goroutines finir. Sans ce programme, le programme s'achèvera immédiatement après le lancement des goroutines. En plus de dormir, il y a plusieurs façons d'attendre qu'un goroutine se termine.

Synchroniser les Goroutines

Une autre façon d’attendre la fin des travaux est d’utiliser des groupes de synchronisation. Vous déclarez un objet de groupe d'attente et le transmettez à chaque goroutine, qui est chargée d'appeler son Terminé() méthode quand c'est fait. Ensuite, vous attendez le groupe de synchronisation. Voici le code qui adapte l'exemple précédent pour utiliser un groupe d'attente.

package principal d'importation ("fmt" "sync" "heure") func foo (date / heure, wg * sync.WaitGroup) d * = 1000000000 fois.Sommeil (d) fmt.Println (d) wg.Done ()  func main () var wg sync.WaitGroup wg.Add (3) go foo (3, & wg) go foo (2, & wg) go foo (1, & wg) wg.Wait ()

Canaux

Les chaînes permettent aux goroutines (et à votre programme principal) d’échanger des informations. Vous pouvez créer un canal et le transmettre à une goroutine. Le créateur peut écrire sur le canal et le goroutine peut lire depuis le canal.. 

La direction opposée fonctionne aussi. Go fournit également une syntaxe douce pour les canaux avec des flèches pour indiquer le flux d'informations. Voici encore une autre adaptation de notre programme, dans laquelle les goroutines reçoivent un canal sur lequel elles écrivent quand elles sont terminées, et le programme principal attend de recevoir des messages de toutes les goroutines avant de se terminer..

package principal importation ("fmt" "heure") func foo (d heure.Durée, c chan int) d * = 1000000 000 fois.Avec (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  

Écrire une goroutine

C'est une sorte de tour. Écrire un goroutine revient à écrire n’importe quelle fonction. Vérifiez foo () fonction ci-dessus, qui est appelée dans le même programme en tant que goroutine et en tant que fonction normale.

3. Traitement des erreurs

Comme je l'ai mentionné précédemment, la gestion des erreurs de Go est différente. Les fonctions peuvent renvoyer plusieurs valeurs et, par convention, les fonctions qui peuvent échouer renvoient un objet d'erreur comme dernière valeur renvoyée.. 

Il existe également un mécanisme qui ressemble aux exceptions via la panique() et récupérer() fonctions, mais il est mieux adapté aux situations spéciales. Voici un scénario typique de traitement des erreurs dans lequel bar() la fonction renvoie une erreur, et le principale() la fonction vérifie s'il y a une erreur et l'imprime.

package main import ("fmt" "erreurs") func bar () erreur return errors.New ("quelque chose ne va pas") func main () e: = bar () si e! = nil fmt.Println ( e.Error ()) 

Vérification obligatoire

Si vous assignez l'erreur à une variable et que vous ne la cochez pas, Go va s'énerver..

func main () e: = bar () main.go: 15: e déclarée et non utilisée 

Il y a des façons de le contourner. Vous pouvez simplement ne pas attribuer l'erreur du tout:

func main () bar ()

Ou vous pouvez l'assigner au trait de soulignement:

func main () _ = bar ()

Support linguistique

Les erreurs ne sont que des valeurs que vous pouvez transmettre librement. Go fournit un petit support d'erreur en déclarant l'interface d'erreur qui nécessite simplement une méthode nommée Erreur() qui retourne une chaîne:

type error interface Error () string 

Il y a aussi le les erreurs package qui vous permet de créer de nouveaux objets d'erreur. le fmt paquet fournit un Errorf () fonction pour créer des objets d'erreur formatés. C'est à peu près ça.

Interaction avec les Goroutines

Vous ne pouvez pas renvoyer d'erreurs (ou de tout autre objet) depuis une goroutine. Les Goroutines peuvent communiquer des erreurs au monde extérieur par un autre moyen. Passer un canal d'erreur à un goroutine est considéré comme une bonne pratique. Les Goroutines peuvent également écrire des erreurs dans les fichiers journaux ou la base de données ou appeler des services distants..

Conclusion

Go a connu un succès et un élan formidables au cours des dernières années. C'est le langage de référence (voir ce que j'ai fait ici) pour les systèmes distribués modernes et les bases de données. Il a beaucoup de développeurs Python convertis. 

Une grande partie de cela est sans aucun doute due au soutien de Google. Mais Go a définitivement ses mérites. Son approche de la conception de langage de base est très différente des autres langages de programmation contemporains. Essaie. Il est facile à prendre en main et amusant à programmer.