Feuille de triche Golang

Apprenez Golang avec des Labs Pratiques

Apprenez la programmation Go grâce à des laboratoires pratiques et des scénarios réels. LabEx propose des cours Go complets couvrant la syntaxe essentielle, les modèles de concurrence, la gestion des erreurs et les techniques avancées. Maîtrisez les fonctionnalités uniques de Go telles que les goroutines, les canaux et les interfaces pour créer des applications concurrentes et efficaces.

Installation et Configuration

Installer Go : Télécharger et Extraire

Téléchargez et installez Go depuis le site officiel.

# Télécharger depuis https://golang.org/dl/
# Linux/macOS - extraire dans /usr/local
sudo tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
# Ajouter à PATH dans ~/.bashrc ou ~/.zshrc
export PATH=$PATH:/usr/local/go/bin
# Vérifier l'installation
go version

Gestionnaire de Paquets : Utilisation de Homebrew (macOS)

Installer Go en utilisant Homebrew sur macOS.

# Installer Go avec Homebrew
brew install go
# Vérifier l'installation
go version
go env GOPATH

Installation sous Windows

Installer Go sur les systèmes Windows.

# Télécharger le programme d'installation .msi depuis https://golang.org/dl/
# Exécuter l'installateur et suivre les instructions
# Vérifier dans l'Invite de commandes
go version
echo %GOPATH%

Configuration de l’Espace de Travail : go mod init

Initialiser un nouveau module et un espace de travail Go.

# Créer un nouveau répertoire et initialiser le module
mkdir my-go-project
cd my-go-project
go mod init my-go-project
# Créer main.go
echo 'package main
import "fmt"
func main() {
    fmt.Println("Hello, World!")
}' > main.go
# Exécuter le programme
go run main.go

Variables d’Environnement

Variables d’environnement Go importantes.

# Afficher toutes les variables d'environnement Go
go env
# Variables clés
go env GOROOT    # Répertoire d'installation de Go
go env GOPATH    # Répertoire de l'espace de travail
go env GOOS      # Système d'exploitation
go env GOARCH    # Architecture

Configuration de l’IDE : VS Code

Configurer VS Code pour le développement Go.

# Installer l'extension Go dans VS Code
# Ctrl+Shift+P -> Go: Install/Update Tools
# Fonctionnalités clés activées :
# - Coloration syntaxique
# - IntelliSense
# - Débogage
# - Intégration des tests

Syntaxe et Types de Base

Package et Imports

Chaque fichier Go commence par une déclaration de package et des imports.

package main
import (
    "fmt"
    "strings"
    "time"
)
// Import unique
import "os"

Variables et Constantes

Déclarer et initialiser des variables et des constantes.

// Déclarations de variables
var name string = "Go"
var age int = 15
var isOpen bool
// Déclaration courte
name := "Golang"
count := 42
// Constantes
const Pi = 3.14159
const Message = "Hello, Go!"
Quiz

Connectez-vous pour répondre à ce quiz et suivre votre progression d'apprentissage

Quelle est la différence entre var name string = "Go" et name := "Go" ?
Il n'y a aucune différence
:= est une déclaration courte qui infère le type, var déclare explicitement le type
:= ne peut être utilisé que pour les constantes
var ne peut être utilisé qu'à l'intérieur des fonctions

Types de Données de Base

Types fondamentaux disponibles en Go.

// Types numériques
var i int = 42
var f float64 = 3.14
var c complex64 = 1 + 2i
// Types texte
var s string = "Hello"
var r rune = 'A'
// Booléen
var b bool = true

Flux de Contrôle

Conditionnelles : if / else / switch

Contrôler le flux d’exécution du programme avec des instructions conditionnelles.

// Instructions If
if age >= 18 {
    fmt.Println("Adulte")
} else if age >= 13 {
    fmt.Println("Adolescent")
} else {
    fmt.Println("Enfant")
}
// Instructions Switch
switch day {
case "Lundi":
    fmt.Println("Début de la semaine de travail")
case "Vendredi":
    fmt.Println("TGIF")
default:
    fmt.Println("Journée normale")
}

Boucles : for / range

Itérer en utilisant diverses constructions de boucles.

// Boucle for traditionnelle
for i := 0; i < 10; i++ {
    fmt.Println(i)
}
// Boucle de style While
for condition {
    // corps de la boucle
}
// Boucle infinie
for {
    // break quand nécessaire
}

Itération avec Range

Itérer sur des slices, des tableaux, des maps et des chaînes de caractères.

// Itérer sur un slice
numbers := []int{1, 2, 3, 4, 5}
for index, value := range numbers {
    fmt.Printf("Index: %d, Valeur: %d\n", index, value)
}
// Itérer sur une map
colors := map[string]string{"red": "#FF0000", "green": "#00FF00"}
for key, value := range colors {
    fmt.Printf("%s: %s\n", key, value)
}
// Itérer sur une chaîne
for i, char := range "Hello" {
    fmt.Printf("%d: %c\n", i, char)
}
Quiz

Connectez-vous pour répondre à ce quiz et suivre votre progression d'apprentissage

Que retourne range lors de l'itération sur un slice en Go ?
Seulement la valeur
L'index et la valeur
Seulement l'index
La longueur du slice

Instructions de Contrôle : break / continue

Contrôler le flux d’exécution de la boucle.

// Sortir de la boucle
for i := 0; i < 10; i++ {
    if i == 5 {
        break
    }
    fmt.Println(i)
}
// Sauter l'itération courante
for i := 0; i < 5; i++ {
    if i == 2 {
        continue
    }
    fmt.Println(i)
}
Quiz

Connectez-vous pour répondre à ce quiz et suivre votre progression d'apprentissage

Quelle est la différence entre break et continue dans les boucles Go ?
Il n'y a aucune différence
break saute l'itération courante, continue quitte la boucle
break quitte complètement la boucle, continue passe à l'itération suivante
Les deux quittent la boucle

Fonctions

Déclaration de Fonction : func

Définir des fonctions avec des paramètres et des valeurs de retour.

// Fonction de base
func greet(name string) {
    fmt.Printf("Hello, %s!\n", name)
}
// Fonction avec valeur de retour
func add(a, b int) int {
    return a + b
}
// Valeurs de retour multiples
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division par zéro")
    }
    return a / b, nil
}

Retours Nommés et Fonctions Variadiques

Fonctionnalités avancées des fonctions et modèles.

// Valeurs de retour nommées
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return // retour nu
}
// Fonction variadique
func sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}
// Utilisation
result := sum(1, 2, 3, 4, 5)

Types de Fonctions et Fermetures (Closures)

Les fonctions comme citoyens de première classe en Go.

// Fonction comme variable
var multiply func(int, int) int
multiply = func(a, b int) int {
    return a * b
}
// Fonction anonyme
square := func(x int) int {
    return x * x
}
// Closure
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}
// Utilisation
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2

Instruction Defer

Différer l’exécution des fonctions jusqu’à ce que la fonction environnante retourne.

func processFile(filename string) {
    file, err := os.Open(filename)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close() // Exécuté lorsque la fonction retourne

    // Traiter le contenu du fichier
    // file.Close() sera appelé automatiquement
}

Structures de Données

Tableaux et Slices

Séquences d’éléments fixes et dynamiques.

// Tableaux (taille fixe)
var arr [5]int = [5]int{1, 2, 3, 4, 5}
shortArr := [3]string{"a", "b", "c"}
// Slices (dynamiques)
var slice []int
slice = append(slice, 1, 2, 3)
// Créer un slice avec capacité
numbers := make([]int, 5, 10) // longueur 5, capacité 10
// Opérations sur les slices
slice2 := slice[1:3]  // [2, 3]
copy(slice2, slice)   // Copier les éléments

Maps

Paires clé-valeur pour des recherches efficaces.

// Déclaration et initialisation de map
var m map[string]int
m = make(map[string]int)
// Déclaration courte
ages := map[string]int{
    "Alice": 30,
    "Bob":   25,
    "Carol": 35,
}
// Opérations sur les maps
ages["David"] = 40        // Ajouter/mettre à jour
delete(ages, "Bob")       // Supprimer
age, exists := ages["Alice"] // Vérifier l'existence

Structs

Regrouper des données connexes avec des types personnalisés.

// Définition de struct
type Person struct {
    Name    string
    Age     int
    Email   string
}
// Créer des instances de struct
p1 := Person{
    Name:  "Alice",
    Age:   30,
    Email: "alice@example.com",
}
p2 := Person{"Bob", 25, "bob@example.com"}
// Accéder aux champs
fmt.Println(p1.Name)
p1.Age = 31

Pointeur

Référence les adresses mémoire des variables.

// Déclaration de pointeur
var p *int
num := 42
p = &num  // Adresse de num
// Déréférencement
fmt.Println(*p) // Valeur à l'adresse (42)
*p = 100        // Changer la valeur via le pointeur
// Pointeur avec structs
person := &Person{Name: "Alice", Age: 30}
person.Age = 31  // Déréférencement automatique

Méthodes et Interfaces

Méthodes

Attacher des fonctionnalités à des types personnalisés.

type Rectangle struct {
    Width, Height float64
}
// Méthode avec récepteur
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}
// Récepteur pointeur (peut modifier)
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}
// Utilisation
rect := Rectangle{Width: 10, Height: 5}
fmt.Println(rect.Area()) // 50
rect.Scale(2)            // Modifie rect

Interfaces

Définir des contrats que les types doivent satisfaire.

// Définition d'interface
type Shape interface {
    Area() float64
    Perimeter() float64
}
// Implémenter l'interface pour Rectangle
func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}
// Rectangle implémente maintenant l'interface Shape
func printShapeInfo(s Shape) {
    fmt.Printf("Aire: %.2f, Périmètre: %.2f\n",
               s.Area(), s.Perimeter())
}

Interface Vide et Assertions de Type

Travailler avec des valeurs de types inconnus.

// L'interface vide peut contenir n'importe quelle valeur
var i interface{}
i = 42
i = "hello"
i = []int{1, 2, 3}
// Assertion de type
str, ok := i.(string)
if ok {
    fmt.Printf("Valeur chaîne : %s\n", str)
}
// Switch de type
switch v := i.(type) {
case int:
    fmt.Printf("Entier : %d\n", v)
case string:
    fmt.Printf("Chaîne : %s\n", v)
default:
    fmt.Printf("Type inconnu : %T\n", v)
}

Intégration (Embedding)

Composer des types en intégrant d’autres types.

type Person struct {
    Name string
    Age  int
}
type Employee struct {
    Person    // Struct intégrée
    Company   string
    Salary    float64
}
// Utilisation
emp := Employee{
    Person:  Person{Name: "Alice", Age: 30},
    Company: "TechCorp",
    Salary:  75000,
}
// Accéder directement aux champs intégrés
fmt.Println(emp.Name) // "Alice"

Gestion des Erreurs

Gestion des Erreurs de Base

Utiliser l’interface d’erreur intégrée pour la gestion des erreurs.

import "errors"
// Fonction qui retourne une erreur
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division par zéro")
    }
    return a / b, nil
}
// Vérification des erreurs
result, err := divide(10, 2)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Résultat: %.2f\n", result)

Erreurs Personnalisées

Créer des types d’erreurs personnalisés pour des conditions d’erreur spécifiques.

// Type d'erreur personnalisé
type ValidationError struct {
    Field   string
    Message string
}
func (e ValidationError) Error() string {
    return fmt.Sprintf("erreur de validation dans %s: %s",
                       e.Field, e.Message)
}
// Fonction utilisant une erreur personnalisée
func validateAge(age int) error {
    if age < 0 {
        return ValidationError{
            Field:   "age",
            Message: "doit être non négatif",
        }
    }
    return nil
}

Enveloppement d’Erreurs (Error Wrapping)

Ajouter du contexte aux erreurs tout en préservant l’erreur d’origine.

import "fmt"
// Envelopper une erreur avec un contexte supplémentaire
func processFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return fmt.Errorf("impossible d'ouvrir le fichier %s: %w",
                          filename, err)
    }
    defer file.Close()

    // Traiter le fichier...
    return nil
}
// Déballer les erreurs
err := processFile("missing.txt")
if err != nil {
    var pathErr *os.PathError
    if errors.As(err, &pathErr) {
        fmt.Println("Erreur de chemin :", pathErr)
    }
}

Panic et Recovery

Gérer les situations exceptionnelles avec panic et recover.

// Fonction qui pourrait paniquer
func riskyOperation() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Récupéré de la panique :", r)
        }
    }()

    // Ceci provoquera une panique
    panic("quelque chose s'est mal passé !")
}
// Utilisation
riskyOperation() // Le programme continue après la panique

Concurrence

Goroutines

Threads légers gérés par le runtime Go.

import "time"
// Goroutine simple
func sayHello() {
    fmt.Println("Hello depuis la goroutine !")
}
func main() {
    // Démarrer la goroutine
    go sayHello()

    // Goroutine anonyme
    go func() {
        fmt.Println("Goroutine anonyme")
    }()

    // Attendre que les goroutines se terminent
    time.Sleep(time.Second)
}

Canaux (Channels)

Communication entre goroutines à l’aide de canaux.

// Créer un canal
ch := make(chan int)
// Canal tamponné (buffered)
buffered := make(chan string, 3)
// Envoyer et recevoir
go func() {
    ch <- 42  // Envoyer la valeur
}()
value := <-ch  // Recevoir la valeur
// Fermer le canal
close(ch)

Modèles de Canaux

Modèles courants pour la communication par canaux.

// Modèle de travailleur (Worker pattern)
func worker(id int, jobs <-chan int, results chan<- int) {
    for job := range jobs {
        fmt.Printf("Travailleur %d traitant la tâche %d\n", id, job)
        results <- job * 2
    }
}
// Modèle Fan-out
jobs := make(chan int, 100)
results := make(chan int, 100)
// Démarrer les travailleurs
for w := 1; w <= 3; w++ {
    go worker(w, jobs, results)
}
// Envoyer les tâches
for j := 1; j <= 5; j++ {
    jobs <- j
}
close(jobs)

Instruction Select

Gérer plusieurs opérations de canal simultanément.

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(time.Second)
        ch1 <- "depuis ch1"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "depuis ch2"
    }()

    // Sélectionner le premier canal disponible
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    case <-time.After(3 * time.Second):
        fmt.Println("délai d'attente dépassé")
    }
}

E/S de Fichiers et JSON

Opérations sur les Fichiers

Lire et écrire des fichiers en utilisant diverses méthodes.

import (
    "io/ioutil"
    "os"
)
// Lire le fichier entier
data, err := ioutil.ReadFile("file.txt")
if err != nil {
    log.Fatal(err)
}
content := string(data)
// Écrire dans un fichier
text := "Hello, World!"
err = ioutil.WriteFile("output.txt", []byte(text), 0644)
// Ouvrir un fichier avec plus de contrôle
file, err := os.Open("data.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

Gestion des CSV

Lire et écrire des fichiers CSV.

import (
    "encoding/csv"
    "os"
)
// Lire CSV
file, _ := os.Open("data.csv")
defer file.Close()
reader := csv.NewReader(file)
records, _ := reader.ReadAll()
// Écrire CSV
file, _ = os.Create("output.csv")
defer file.Close()
writer := csv.NewWriter(file)
defer writer.Flush()
writer.Write([]string{"Nom", "Âge", "Ville"})
writer.Write([]string{"Alice", "30", "NYC"})

Traitement JSON

Encoder et décoder des données JSON.

import "encoding/json"
// Struct pour le mappage JSON
type Person struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email,omitempty"`
}
// Marshal (Go vers JSON)
person := Person{Name: "Alice", Age: 30}
jsonData, err := json.Marshal(person)
if err != nil {
    log.Fatal(err)
}
// Unmarshal (JSON vers Go)
var p Person
err = json.Unmarshal(jsonData, &p)

Requêtes HTTP

Effectuer des requêtes HTTP et gérer les réponses.

import "net/http"
// Requête GET
resp, err := http.Get("https://api.github.com/users/octocat")
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
// Requête POST avec JSON
jsonData := []byte(`{"name":"Alice","age":30}`)
resp, err = http.Post("https://api.example.com/users",
                      "application/json",
                      bytes.NewBuffer(jsonData))

Tests

Tests Unitaires : go test

Écrire et exécuter des tests en utilisant le framework de test de Go.

// math.go
package main
func Add(a, b int) int {
    return a + b
}
// math_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
    result := Add(2, 3)
    expected := 5

    if result != expected {
        t.Errorf("Add(2, 3) = %d; attendu %d", result, expected)
    }
}
// Exécuter les tests
// go test
// go test -v (verbeux)

Tests Pilotés par Tableaux (Table-Driven Tests)

Tester plusieurs cas efficacement.

func TestAddMultiple(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"nombres positifs", 2, 3, 5},
        {"avec zéro", 0, 5, 5},
        {"nombres négatifs", -1, -2, -3},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("obtenu %d, attendu %d", result, tt.expected)
            }
        })
    }
}

Benchmarking

Mesurer la performance des fonctions.

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(2, 3)
    }
}
// Exécuter les benchmarks
// go test -bench=.
// go test -bench=BenchmarkAdd -benchmem

Tests d’Exemple

Créer des exemples exécutables qui servent de documentation.

import "fmt"
func ExampleAdd() {
    result := Add(2, 3)
    fmt.Printf("2 + 3 = %d", result)
    // Output: 2 + 3 = 5
}
func ExampleAdd_negative() {
    result := Add(-1, -2)
    fmt.Printf("(-1) + (-2) = %d", result)
    // Output: (-1) + (-2) = -3
}
// Exécuter les exemples
// go test -run Example

Modules et Packages Go

Gestion des Modules

Initialiser et gérer les modules Go pour la gestion des dépendances.

# Initialiser un nouveau module
go mod init github.com/username/project
# Ajouter des dépendances
go get github.com/gorilla/mux
go get -u github.com/gin-gonic/gin  # Mettre à jour vers la dernière version
# Supprimer les dépendances inutilisées
go mod tidy
# Télécharger les dépendances
go mod download
# Dépendances locales (vendor)
go mod vendor

Fichier go.mod

Comprendre le fichier de définition du module.

module github.com/username/myproject
go 1.21
require (
    github.com/gorilla/mux v1.8.0
    github.com/stretchr/testify v1.8.4
)
require (
    github.com/davecgh/go-spew v1.1.1 // indirect
    github.com/pmezard/go-difflib v1.0.0 // indirect
)

Création de Packages

Structurer le code en packages réutilisables.

// Structure du package
// myproject/
//   ├── go.mod
//   ├── main.go
//   └── utils/
//       ├── math.go
//       └── string.go
// utils/math.go
package utils
// Fonction exportée (commence par une majuscule)
func Add(a, b int) int {
    return a + b
}
// Fonction privée (commence par une minuscule)
func multiply(a, b int) int {
    return a * b
}
// main.go
package main
import (
    "fmt"
    "github.com/username/myproject/utils"
)
func main() {
    result := utils.Add(5, 3)
    fmt.Println(result)
}

Commandes Go Courantes

Commandes essentielles pour le développement Go.

# Exécuter un programme Go
go run main.go
# Compiler l'exécutable
go build
go build -o myapp  # Nom personnalisé
# Installer le binaire dans GOPATH/bin
go install
# Formater le code
go fmt ./...
# Vérifier le code pour les problèmes
go vet ./...
# Nettoyer le cache de construction
go clean -cache

Liens Pertinents