Outils pour utilisateurs

Outils du site


dev:go:tutoriels:ecrire_un_module_en_go

Ceci est une ancienne révision du document !


:TODO:

Écrire un module en Go

Ce tutoriel propose de créer deux modules:

  • Le premier sera une bibliothèque prévue pour être importée par d'autres bibliothèques ou applications ;
  • Le second est une application appelante utilisant le premier.

Dans ce tutoriel on va :

  1. Créer un module – Implémenter un petit module contenant des fonctions qui pourront être appelées depuis un autre module ;
  2. Appeler le code depuis un autre module – Importer et utiliser notre nouveau module ;
  3. Retourner et contrôler une erreur – Ajouter un contrôle élémentaire des erreurs ;
  4. Retourner une valeur quelconque – Manipuler des données dans des slices (tableaux à taille dynamique en Go) ;
  5. Retourner des valeurs pour l'utilisateur – Stocker des couples clé/valeur dans un map ;
  6. Ajouter des test – Utiliser les fonctionnalités intégrés de Go pour les test unitaires du code ;
  7. Compilation et installation de l'application en local.

Un module utilisable par les autres

En Go, le module est l'unité de distribution du code. Il permet de distribuer une collection de paquetages. Par exemple on peut vouloir créer un module contenant des paquetages groupant des fonctions d'analyse financière. Toutes les applications ayant besoin de faire de l'analyse financière pourront bénéficier du code. Pour plus d'informations voir la documentation officielle développement et publication des modules.

Le code en Go est regroupé en paquetages et les paquetages sont groupés et distribués dans des modules. Le module déclare les dépendances nécessaires pour l'exécution du code incluant la version de Go et l'ensemble des autres modules requis.

Lorsqu'on ajouter ou améliore les fonctionnalités d'un module, on publie une nouvelle version du module. Les développeurs qui appellent/utilisent les fonctions de votre module mettent à jour les paquetages et testent les nouvelles versions avant de passer en production.

Déclarer votre nouveau module en utilisant la commande go mod init et en fournissant le chemin vers le module. Si vous souhaitez publier votre module se devra être un chemin depuis lequel le module sera téléchargeable par les outils Go : cela pourrait être votre dépôt de code.

# créer un repertoire pour le code
mkdir greetings && cd greetings
 
# initialise le module
go mod init example.com/greetings

La commande go mod init crée le fichier go.mod qui permet le suivi des dépendances. Pour le moment le fichier ne contient que le nom du module et la version de Go supportée.

cat go.mod 
module example.com/greetings
 
go 1.20

Lorsque des dépendances seront ajoutées, le fichier go.mod indiquera les versions des modules desquelles dépend votre code. Cela permet de garder la compilation reproductible et offre un contrôle direct sur les versions des modules à utiliser.

Via votre éditeur créer le fichier greetings.go avec le contenu suivant :

greetings.go
package greetings
 
import "fmt"
 
// Hello retourne un message de bienvenue à la personne désignée
func Hello( name string) string {
	message := fmt.Sprintf("Salut, %v. Bienvenue!", name)
	return message
}

Dans ce code on a:

  • Déclarer un paquetage nommé “greetings” regroupant des fonction comme Hello() ;
  • Définit la fonction Hello() pour retourner un message de salutation.

La fonction Hello() prend un argument nommé de type chaîne de caractères et retourne une chaîne de caractères. En Go les fonctions commençant par une majuscule peuvent être appelées en dehors du paquetage. En Go ce comportement est désigné “export des noms”.

L'opérateur := est un raccourci permettant de déclarer et d'initialiser une variable en une seule ligne. Go utilise l'instruction à droite de l'opérateur pour déterminer le type de la variable.

Appel du code depuis un autre module

Nous allons ici écrire le code appelant la fonction Hello() du module greetings.

Créer un répertoire hello au même niveau que greetings

cd ..
mkdir hello

Après cette opération on a donc l'arborescence suivante

.
├── grettings
│   ├── go.mod
│   └── greetings.go
└── hello

On entre dans le répertoire hello et on initialise le suivi du module :

go mod init example.com/hello

Ouvrir l'éditeur et créer le fichier hello.go avec le contenu suivant :

hello.go
package main
 
import (
	"fmt"
 
	"example.com/greetings"
)
 
func main() {
	msg := greetings.Hello("Yoann")
	fmt.Println(msg)
}

Dans ce code :

  • On déclare un paquetage “main”. En Go, un code exécuté en tant qu'application doit être dans un paquetage “main” ;
  • On importe deux paquetages : “example.com/greetings” et “fmt”. Cela permet d'accéder aux fonctions de ces paquetages ;
  • On affiche le message via l'appel de la fonction Hello() dans le paquetage greetings.

On doit maintenant éditer le module “example.com/hello” pour qu'il utilise le module local “example.com/greetings”.

Dans le cas où le module est publié les outils Go peuvent le télécharger. Ici ce n'est pas le cas : il faut donc adapter le module “example.com/hello” pour qu'il puisse trouver le module “example.com/greetings” sur le système de fichier local. Depuis la ligne de commande :

go mod edit -replace example.com/greetings=../grettings

La commande spécifie que “example.com/greetings” devra être remplacé par “../grettings” lors du calcul de dépendances. Après exécution de la commande, le fichier go.mod doit contenir une directive replace :

cat go.mod 
module example.com/hello
 
go 1.20
 
replace example.com/greetings => ../greetings

Toujours depuis la ligne de commande dans le répertoire hello, exécuter la commande go mod tidy pour synchroniser les dépendances du module “example.com/hello”, ajoutant ce qui est requis par le code mais pas encore suivi dans le module.

go mod tidy

Après exécution de la commande le ficheir go.mod doit être de la forme :

module example.com/hello

go 1.20

replace example.com/greetings => ../grettings

require example.com/greetings v0.0.0-00010101000000-000000000000

La commande a bien trouvé le code dans le répertoire local ../grettings et introduit la directive require. Cette dépendance a été créée par l'import du paquetage greetings dans hello.go

LEn complément du module on retrouve une pseudo-version générée à la place de la version semantique (semantic version number). La documentation officielle décrit en détail le versionning des modules.

go run .
Salut, Yoann. Bienvenue!

Retourner et contrôler une erreur

La gestion des erreurs est nécessaire à la production d' un code solide. On va maintenant aborder la génération d'une erreur depuis le module 'greetings' et sa gestion par le module appelant 'main'.

Via l'éditeur, on modifie le fichier greetings/greetings.go comme proposé ci-dessous:

greetings.go
package greetings
 
import (
    "errors"
    "fmt"
)
 
// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
    // If no name was given, return an error with a message.
    if name == "" {
        return "", errors.New("empty name")
    }
 
    // If a name was received, return a value that embeds the name
    // in a greeting message.
    message := fmt.Sprintf("Hi, %v. Welcome!", name)
    return message, nil
}

A propos des modifications apportées:

  • La fonction Hello() retourne deux valeurs de type 'string' et 'error'. L'appelant devra vérifier la seconde valeur pour évaluer si une erreur s'est produit. Toutes les fonctions Go peuvent retourner plusieurs valeurs ;
  • On a importé le paquetage “errors” de la bibliothèque standard Go et on a utiliser la fonction errors.New() pour générer un erreur ;
  • Une instruction “if” permet de générer une si la valeur de l'argument name est incorrecte ;
  • Le retour normal de la fonction contient la valeur nil en seconde valeur.

On peut à présent modifier le code appelant dans le fichier hello/hello.go :

hello.go
package main
 
import (
	"fmt"
	"log"
 
	"example.com/greetings"
)
 
func main() {
	//Définition de propriétés du Logger
	log.Default().SetPrefix("greetings: ")
	//Désactive l'afficahge du timestamp, du fichier source et du numero
	//de ligne
	log.SetFlags((0))
 
	msg, err := greetings.Hello("Yoann")
 
	//Si une erreur est retournée, elle est affichée dans la console
	//et le programme s'arrête
	if err != nil {
		log.Fatal(err)
	}
 
	//Affiche le message dans la console
	fmt.Println(msg)
}

Références

dev/go/tutoriels/ecrire_un_module_en_go.1691081248.txt.gz · Dernière modification : 2023/08/03 16:47 de yoann