Fév

Vous êtes lassé de votre langage préféré, vous en voulez un plus simple, plus rapide et qui compile instantanément ? J’ai trouvé ce qu’il vous faut. Let’s Go.

Un peu d’histoire…

En 2007, trois développeurs de Google : Robert Griesemer, Rob Pike, et Ken Thompson n’étaient pas satisfait de la performance de leur logiciel développé en C++. Suite à une conférence sur C++ version 11, ils trouvent que le langage devient de plus en plus complexe d’une part et d’autre part, ils remarquent que la compilation de leur monolithe prenait plus d’une heure.

Pendant ce long moment de compilation, certains d’entre vous auraient probablement pris une pause, regardé un épisode ou parcouru les articles de notre blog, eux ont tout simplement décidé de définir les spécifications d’un nouveau langage qui sera performant, portable et avec une prise en charge simplifiée du multithreading.

De plus, ce langage devra avoir une compilation rapide, une gestion simplifiée de la mémoire et devra être sans hiérarchie.

Ces spécifications définiront les bases d’un nouveau langage de programmation orienté Ops : le langage Go est né !

Qui utilise ce langage ?

Go, aussi appelé Golang, était un langage initialement utilisé en interne par Google puis il a gagné en reconnaissance et il est maintenant embarqué dans des projets de R&D à grande échelle comme Docker, Kubernetes et Terraform. Go a évolué !

Les fondations 

La syntaxe de Go est héritée du langage C : on retrouve la notion de structure et de pointeur.
Oui je parle bien des pointeurs, une notion qui résonne comme un cauchemar pour certains. L’objectif de cet article n’est pas d’expliquer en détail ce qu’est un pointeur mais pour la suite de votre lecture, retenez qu’un pointeur est une variable particulière qui contient l’adresse mémoire d’une autre variable.

Maintenant que le pointeur a été dédiabolisé, revenons aux fondations de ce merveilleux langage.

En plus des structures et des pointeurs, Go est un langage bas niveau, compilé et fortement typé.

Contrairement aux autres langages bas niveau, il n’y a pas d’arithmétique de pointeur ni de gestion manuelle de la mémoire. De plus, le typage des variables est implicite.

Comme évoqué précédemment, en Go nous organisons les données sous forme de structure. Une structure peut être enrichie en associant à ses attributs des tags qui seront utilisés par certaines bibliothèques. Attardons-nous sur ces deux premiers concepts de Go : la structure et ses tags !

Il faut voir les tags comme des annotations qui permettent de décrire et/ou de donner des instructions à tous ceux qui vont interagir avec cette structure.

Voici l’exemple d’une structure avec des tags pour illustrer mon propos :

type Person struct {

   FirstName string `json:”prenom” gorm:”column:firstname” validate:”required”`

   Age       uint8 `json:”-” gorm:”-” validate:"gte=0,lte=100" `

   Email     string`json:”email,omitempty” validate:"required,email"`
}

Explications :

  • Le tag ‘json’ permet de préciser le nom de la propriété lors de l’exportation au format JSON.
  • l’ORM Gorm utilise les tags pour associer une variable à une colonne de la base de données.
  • La bibliothèque validator utilise le tag validate qui définit des contraintes aux valeurs attendues.

La structure et ses tags forment un tout, on définit en une seule fois le nom de l’élément, son type, les valeurs autorisées puis la correspondance à la base de données.
Après avoir compris ces deux premiers concepts, passons à des notions plus avancées.

Composition et interface

En Go, il est possible de créer une nouvelle structure en imbriquant une ou plusieurs structures. Alors oui potentiellement cela peut donner une structure qui contient une structure, qui elle-même est composée d’une structure etc. Cette alternative à l’héritage s’appelle la composition. Voilà notre troisième concept ! La différence avec l’héritage c’est que la composition permet de ne pas avoir de hiérarchie. Autrement dit, une structure qui compose une autre structure ne modifie pas les propriétés de la structure.

En plus de la composition, il existe également le mécanisme d’interface en Go.

Si vous avez l’habitude de manipuler les interfaces dans d’autres langages comme C# ou Java par exemple, le principe est le même. L’interface est composée
d’une liste de signatures et la structure les implémente.

La subtilité en Go, c’est qu’il n’y a pas de relation explicite entre une interface et une structure. La seule règle à retenir est la suivante :

Si une structure implémente toutes les signatures de l’interface alors elle implémente automatiquement l’interface sinon non. Passons justement à l’implémentation de ces signatures grâce aux fonctions.

Particularité des fonctions

Chaque fonction déclarée avec le mot clé func est suivie de son nom, de ses paramètres d’entrée puis de ses variables de sortie.

Oui vous avez bien lu, une fonction peut retourner plusieurs valeurs. Par convention, on renvoie toujours les erreurs en dernier avec le type error. S’il est nul c’est qu’il n’y pas d’erreur, dans le cas contraire on peut afficher le message d’erreur.

De plus, il est possible d’exécuter une instruction après avoir renvoyé le résultat grâce au mot clé defer. Ce dernier est utilisé juste après une connexion pour prévoir sa fermeture.

Si vous voulez faire une programmation orientée objet, c’est possible !

Pour cela, on peut associer à une structure une fonction en spécifiant un pointeur de structure, appelé receiver, avant le nom de la fonction, cette fonction devient donc une méthode associée à une structure.

Exemple

package main

import (
"fmt"
)

// déclaration de la structure Engine
type Engine struct {
   speed int
}

// déclaration de la structure Car composée de la structure Engine
type Car struct {
// name est de type string et a un tag json
   name string `json:”name”`

   Engine
}

// la fonction getName est une méthode de la structure Car
func (c *Car) getName() string {
   return c.name
}

func main() {
// déclaration d’une variable doloreane de type Car
   doloreane := Car{name: "doloreane", Engine: Engine {speed: 2.21}}

// appel de la méthode getName()
   fmt.Println(doloreane.getName())
}

Dans ce bref exemple, nous revoyons les structures, le concept de composition, les tags et surtout la notion de fonction avec en bonus une simulation de l’orienté objet avec les pointeurs de structure, ici c *Car.

Dans le cas de structures composées qui ont une méthode en commun, il faudra explicitement choisir celle que vous voulez utiliser.

Après tous ces concepts techniques qui définissent les particularités de Go, passons à l’organisation du code.

Organiser son code

Go permet de regrouper des fichiers (.go) dans des dossiers appelés des packages. Pour importer les fonctions et les structures d’un autre package, on utilise le mot clé import. Une bibliothèque externe a son propre package. Cependant organisez bien vos packages car les imports cycliques ne sont pas autorisés.

Quelle est la visibilité de mes fonctions entre les packages ?

En Go, tout ce qui a une visibilité privée à l’intérieur du package commence par une lettre minuscule. Tout ce qui commence par une majuscule est publique et utilisable en dehors du package.

La gestion des bibliothèques est faite par les go modules qui ajoutent automatiquement les dépendances au fichier go.mod à partir des instructions d’importation et vérifient qu’elles sont à jour lors de la compilation.

Une des bibliothèques les plus connues est gin. Elle permet d’écrire facilement des API. Elle permet aussi de définir explicitement les spécifications de routage, le code erreur et le format de retour. Il n’y a pas d’annotation mais des paramètres optionnels.

Grâce à cette bibliothèque très pratique, nous avons ainsi toutes les règles de routage qui sont définies au même endroit.

Exemple d’une api en go: 

package main

import (
"net/http"
"github.com/gin-gonic/gin"
)

func setupRouter() *gin.Engine {

   // Disable Console Color
   gin.DisableConsoleColor()

   r := gin.Default()

   // Ping test
   r.GET("/ping", func(c *gin.Context) {
      c.String(http.StatusOK, "pong")
   })

   // Get user value
   r.GET("/user/:name", func(c *gin.Context) {
      user := c.Params.ByName("name")
      c.JSON(http.StatusOK, gin.H{"message": “bienvenue a “+name})
   })

   return r
}

func main() {

   r := setupRouter()

   // Listen and Serve in :8080
   r.Run(":8080")
}

Cet exemple d’api illustre les avantages qu’apporte gin avec une écriture simplifiée dans la création et la gestion du retour de nos deux routes “/ping” et “/user/:name”.

Les goroutines

Go permet de simplifier les appels concurrents en utilisant les goroutines.

Une goroutine est une fonction qui sera exécutée  dans un thread. Pour en créer une, il suffit d’ajouter l’instruction go devant l’appel d’une fonction. Très pratique par exemple pour faire plusieurs appels à une API en parallèle.

Les goroutines peuvent communiquer entre elles grâce à des channels et se coordonner avec des selects.

Ces deux dernières notions, que je ne vais pas détailler dans cet article car elles mériteraient à elles seules un article complet, sont très importantes si vous souhaitez aller plus loin dans votre apprentissage des goroutines.

Faut-il apprendre Go ?

La première prise en main de Go est déroutante, on se rappelle des mauvais souvenirs lors de la manipulation des pointeurs du langage C. Il faut voir les pointeurs sur structure comme la référence d’un objet. Tout commence par une structure de données à laquelle on va ajouter quelques fonctions. Le code est simple à écrire et peu verbeux.

En 2020, le développeur Go est une perle rare très recherchée, démarquez-vous !

Côme Burguburu

Liens utiles

Le tour de Go permet d’apprendre les différents concepts du langage
https://go-tour-fr.appspot.com/

Des exemples de codes
https://gobyexample.com/

Installer Go
https://golang.org/doc/install

Le Go playground est un service web qui permet de tester des programmes en Go, certains exemples sont fournis
https://play.golang.org/

JSON to Go est un service qui permet de convertir un json en structure Go
https://mholt.github.io/json-to-go/

Go cheat sheet
https://devhints.io/go

Une bibliothèque pour manipuler les tableaux
https://github.com/thoas/go-funk

Un billet pour comprendre les Go modules
https://gauthier.frama.io/post/gomodules/

Related Posts

Leave A Comment