Apprendre le langage GoLang facilement
Apprendre le langage GoLang facilement
« Chez Google, nous pensons que la programmation devrait être rapide, productive et
surtout, fun. C’est pourquoi nous sommes ravis de proposer ce nouveau langage de
programmation expérimental. Les opérations de compilation sont presque
instantanées, et le code compilé propose une vitesse de fonctionnement proche de
celle du C ».
Go est souvent comparé au langage Python car tous les 2 se veulent très simples
syntaxiquement. Personnellement je trouve python est plus simple syntaxiquement
mais Python reste tout de même un langage interprété, contrairement à Go qui est un
langage compilé.
Certains d'entre vous ont peut être utilisé que des langages de programmation
interprétés, si c'est le cas alors une petite explication s'impose pour ainsi commencer
avec des bonnes bases :
Dans un langage interprété, le code source est interprété, par un autre logiciel nommé
l’interpréteur, celui-ci traduit au fur et à mesure les instructions de votre programme.
Dans un langage compilé, le code source est tout d'abord compilé en langage binaire
c’est une suite de 0 et de 1 uniquement compréhensible par votre machine par un
autre logiciel qu'on appelle le compilateur.
Avantages et inconvénients des langages
interprétés et langages compilés
Un programme écrit dans un langage compilé a l'avantage de ne plus avoir besoin une
fois compilé de programme annexe pour s'exécuter (un langage interprété aura
toujours besoin de son interpréteur), de plus comme votre code est exécuté
directement par votre machine alors le temps d’exécution de votre programme sera en
général plus rapide pour le même programme dans un langage interprété.
Donc pour revenir à notre langage Go, c'est un langage qui se veut accessible et
rapide pour une programmation à grande échelle, il est donc concevable de l'utiliser
aussi bien pour écrire des applications, des scripts ou sur d'autres types des gros
projets.
Utilisation du langage GO
On retrouve le langage Go dans les domaines suivants (liste non exhaustive) :
Serveurs
Web
Systèmes embarqués
IOT (Internet Of Things)
Android
IOS
Jeux-vidéos
etc ...
CloudFlare
CoreOS
DropBox
Docker
Nokia
Ovh
YouTube
SoundCloud
Splice
etc ...
Public visé
Ce tutoriel est conçu pour des programmeurs ou des curieux ayant besoin de
comprendre le langage de programmation Go à partir de zéro. Ce tutoriel vous
donnera une compréhension suffisante du langage de programmation Go, qui vous
permettra d’atteindre des niveaux d’expertise plus élevés.
Guide pour configurer de
votre environnement GoLang
Ce guide de démarrage explique comment configurer un
environnement GO sur Linux et Windows et d'exécuter votre
premier programme GO.
Prérequis
Je vais commencer par vous apprendre à lancer votre tout premier programme Go !
Mais avant de d'exécuter votre programme Go il vous faut :
Un éditeur de texte
Un compilateur GO
Éditeur de Texte
Pour ma part j’utilise un éditeur de texte gratuit et open source à savoir Visual studio
Code avec l’extension Go, c'est une extension qui permet entres autre de :
Libre à vous d’utiliser autre chose, tant que votre éditeur de texte vous permet d'écrire
du texte et de le sauvegarder alors c'est suffisant et vous pouvez passer à l'étape
suivante !
Téléchargez le compilateur Go
Pour transformer votre code source en langage machine afin que votre CPU puisse
exécuter votre programme il faut installer le compilateur GO. Le compilateur Go est
disponible sur différents OS (Linux, Mac OS X et Windows)
Copier
source ~/.bashrc
Copier
Exécuter le fichier MSI et suivez les instructions de votre fenêtre pour installer les
outils Go.
L'architecture de Go
Avant de lancer notre programme, il serait intéressant de comprendre l'architecture de
go.
Déja lors de votre installation des outils, go vous a créé plusieurs variables
d'environnement dont deux variables d'environnements importantes nommées
respectivement :
go envCopier
Si vous vous placez sur votre variable d'environnement GOPATH, vous observerez
l'arborescence suivante :
$GOPATH
├──── bin
├──── pkg
└──── src
package main
import "fmt"
func main() {
[Link]("Hello, World!")
Copier
go run [Link]
Copier
Hello, World!
Bravo vous avez appris à lancer votre premier programme en GO "clap clap"👏 !
Dans le prochain chapitre je vais vous expliquer un peu plus en détail le code que
vous venez d'exécuter.
Explication pas à pas du
programme GO hello world
Ce chapitre vous explique pas à pas le code source du
programme "hello world" écrit sous le langage de
programmation GO et les commentaires en Go.
Explication du code
Reprenons le code du chapitre précédent et détaillons-le :
package main
import "fmt"
func main() {
Copier
Information
Nous verrons avec plus de détails certaines notions dans d'autres chapitres
Le package main
package main
Copier
Les programmes GO ne peuvent s’exécuter que dans des packages, c’est une
déclaration obligatoire pour chaque programme go. Il permet de définir le nom du
paquet dans lequel le programme devrait se trouver. le package main permet
d’informer votre compilateur Go que le paquet doit être compilé en tant que
programme exécutable au lieu d'une bibliothèque partagée (nous verrons cette partie
plus en détail dans un chapitre dédié aux packages).
import "fmt"
Copier
Cette ligne permet d’informer votre compilateur qu’il est nécessaire d’importer la
bibliothèque fmt avant d'exécuter la suite de votre programme.
Information
La fonction main
func main()
Copier
C'est une fonction nommée main() qui représente le point d’entrée du binaire c’est
donc la fonction principale du début de l'exécution de votre programme.
La fonction Println
[Link] (...)
Copier
Les commentaires
[Link]("Hello, World!") // afficher du texte
Copier
le texte situé après les deux slashs (//) est ce qu'on appelle un commentaire
Les commentaires sont des lignes de code qui sont ignorées par votre compilateur, ils
permettent à vous ou à une autre personne qui lit votre code de mieux comprendre
votre code source et inversement si vous lisez le code source de quelqu'un d'autre il
sera plus simple pour vous de mieux vous retrouver dans son code.
Information
Les commentaires sur une seule ligne ou à droite d'une instruction. Pour déclarer ce
type de commentaire il suffit de taper un double slash (// ) suivi de votre
commentaire.
Exemple :
// mon commentaire
[Link]("Hello") // cette fonction permet d'afficher le text Hello
Copier
Les commentaires sur plusieurs lignes. Pour déclarer ce type de commentaire il suffit
de taper un slash suivi d'une étoile ( /*) pour indiquer le début du commentaire suivi
de votre commentaire et enfin une étoile suivie d'une slash ( */) pour indiquer la fin du
commentaire.
Exemple :
/* Je suis un long commentaire
première ligne
deuxième ligne */
Les variables dans le
langage de programmation
Go
Ce chapitre vous explique les variables et les constantes en
GoLang. Vous allez voir les différents types de variables et
d'opérateurs mais aussi apprendre à déclarer, modifier et
calculer vos variables dans le langage de programmation GO.
Définition
Avant de vous montrer comment déclarer et utiliser une variable, il faut d'abord
comprendre ce qu'est une variable.
Rappel
"Go est un langage à typage statique (les types des variables sont connus lors de la
compilation et doivent être spécifiés expressément par le programmeur)"
Une variable est le nom donné à un emplacement mémoire pour stocker une valeur,
elle permet de stocker des données, ces données vont pouvoir être utilisées plus
tard.
Une variable est constituée d'un type. Le type d'une variable permet :
Types entiers
Il y a plusieurs types d'entiers possibles, tout dépend de la valeur que vous voulez
stocker dans votre variable. Je vous présente ci-dessous un tableau qui liste les types
d'entiers qu'il est possible d'utiliser en GoLang ainsi que les nombres qu'ils peuvent
conserver.
Information
Ce type permet de stocker des nombres décimaux (nombres à virgule) : 1.5, 1.6,
128.5
Type description
Types booléens
C'est un type qui va nous permettre par exemple dans un jeu de savoir si est un joueur
est vivant ou pas.
Type string
C'est un type qui nous permet de stocker des chaines de caractères (du texte).
On utilise le mot-clé string pour déclarer une variable de type chaines de caractères
Information
Je n'ai cité que les variables plus utilisées mais il faut savoir qu'il existe d'autres types
de variables que je ne vais pas détailler dans cette séance.
Copier
Exemple :
package main
import (
"fmt"
func main() {
Copier
Résultat :
0
Pour changer la valeur par défaut de la variable, il suffit de la surcharger comme ci-
dessous :
package main
import (
"fmt"
func main() {
[Link](vie)
Copier
Résultat :
12
package main
import (
"fmt"
func main() {
Copier
Résultat :
vie : 0
argent : 0
puissance : 0
Et même de les surcharger !
package main
import (
"fmt"
func main() {
Copier
Résultat :
vie : 10
argent : 20
puissance : 30
package main
import (
"fmt"
func main() {
Copier
Résultat :
vie : 20
nom : Default
vitesse : 5.4
package main
import (
"fmt"
func main() {
var (
vie int = 20
}
Copier
Résultat :
vie : 20
nom : Default
vitesse : 5.4
Typage dynamique en Go
Il est tout à fait possible en GoLang de déclarer des variables dynamiquement (pas
besoin de spécifier le type lors de la déclaration), dans ce cas on va laisser l'ordinateur
choisir pour nous le type de variables en fonction de la valeur qu'on lui a transmise.
Exemple :
package main
import "fmt"
func main() {
/*
*/
Copier
Résultat :
Le type de la varialbe flt est float64
Information
Les constantes
Par définition une constante possède une valeur fixe, elle ne peut en aucun cas être
modifié. Voici comment on déclare une constante.
package main
import "fmt"
func main() {
Copier
Résultat :
ma Constante : 50
package main
import "fmt"
func main() {
maConstante = 50
Copier
Résultat :
.\[Link]:14: cannot assign to maConstante
il nous envoie bouler. Il n'aime pas trop quand on tente de modifier ses constantes 😡.
Les calculs
Il est possible avec le langage Go de faire des opérations très simples, telles quelles :
L'addition
La soustraction
La multiplication
La division
Le modulo (le reste d'une division)
Les opérateurs de calcul
Opérateurs
de calcul Effet
Exemple :
package main
import "fmt"
func main() {
var a int = 4
var b int = 2
Copier
Résultat :
a + b = 6
a - b = 2
a * b = 8
a / b = 2
a % b = 0
Opérateurs
d'assignation Effet
Exemple :
package main
import "fmt"
func main() {
var a int = 4
var b int = 2
a += b
[Link]("a += b = ", a)
a -= b
[Link]("a -= b = ", a)
a *= b
[Link]("a *= b = ", a)
a /= b
[Link]("a /= b = ", a)
a %= 3
[Link]("a %= b = ", a)
Copier
Résultat :
a += b = 6
a -= b = 4
a *= b = 8
a /= b = 4
a %= b = 1
Opérateurs Résultat (x :=
d'incrémentation Syntaxe Effet 9)
Exemple :
package main
import "fmt"
func main() {
var a int = 4
a ++ // incrémentation
[Link]("incrémentation de 1 : ", a)
a -- // décrémentation
[Link]("décrémentation de 1 : ", a)
Copier
Résultat :
incrémentation de 1 : 5
décrémentation de 1 : 4
Voyons voir ceci plus en détails en additionnant deux variables de type int :
package main
import "fmt"
func main() {
var x int = 50
var y int = 30
Copier
Résultat :
x + y = %!(EXTRA int=80)
Information
Ne faites pas attention à au mot-clé EXTRA, l'important ici est le type de retour du calcul
(ici un type int) et la valeur (ici 80)
package main
import "fmt"
func main() {
var x int = 50
Copier
Erreur :
invalid operation: x + y (mismatched types int and float32)
Le compilateur Go nous avertit qu'il n'arrive pas à calculer le résultat avec des types
différents.
Pour éviter ce type erreur, il suffit soit de changer dés le début définitivement le type
de la variable, soit de faire un cast notre variable c'est à dire de convertir la variable
temporairement juste le temps de faire notre calcul pour pouvoir garder son type
original (ici x int) sur la suite de notre code.
package main
import "fmt"
func main() {
var x int = 50
Copier
Résultat :
x + y = %!(EXTRA float32=80.5)
Comme le montre le résultat le type de retour de notre calcul est bien de type float32
car nous avons additionné un type float32 avec un type int convertit en float32.
Les conditions dans le
langage de programmation
Go
Ce chapitre vous explique les conditions en GoLang,. Ils
permettent de tester vos variables. On apprendra à utiliser les
mots-clés if, else if, else et switch et on fera le tour sur les
différents opérateurs existant dans le langage de
programmation Go.
Les prérequis
Maintenant que vous savez comment déclarer vos variables, il est temps de les tester
avec des conditions !
Entrée utilisateur
Avant de vous montrer comment créer des conditions, je vais d'abord vous indiquer
comment enregistrer une entrée utilisateur dans une variable (nous utiliserons les
entrées utilisateur plus tard dans la suite de ce chapitre et dans les prochains
chapitres)
package main
import (
"bufio"
"fmt"
"os"
func main() {
[Link](entreeUtilisateur)
Copier
Résultat :
Entrez quelque chose : quelque chose
quelque chose
Convertir une string en entier
J'ai une autre chose à vous montrer avant de passer à la suite. Pour le moment nous
sommes capables de capturer que des chaines de caractères, dans certains cas vous
aurez besoin de capturer un type int à la place d'un type string pour faire par exemple
des calculs.
Pour ce faire, il suffit de convertir le résultat de votre scanner en int, une fonction
nommée Atoi() de la bibliothèque strconv est disponible pour répondre à ce besoin.
package main
import (
"bufio"
"fmt"
"os"
"strconv"
func main() {
scanner := [Link]([Link])
[Link]()
nbr, _ := [Link]([Link]()) // conversion du type
string en int
Copier
Résultat :
Entrez un nombre entier : 6
res : 12
Les conditions
Maintenant promis on s'attaque aux conditions 😅.
if, else
Pour créer une condition il suffit d'utiliser le mot-clé if (qui se traduit en français par
"si") suivit de votre condition. Ensuite vous ouvrez une accolade { et fermez-la un peu
plus loin }. Tout ce qui se trouve à l'intérieur de vos accolades sera exécuté
uniquement si la condition est bonne.
package main
import (
"bufio"
"fmt"
"os"
"strconv"
func main() {
scanner := [Link]([Link])
[Link]()
if err != nil {
}
if age < 17 { // vérifier si l'utilisateur à au moins 18 ans
[Link]("Sortez !")
[Link]("Entrez :)")
Copier
Résultat :
Entrez votre age : 16
Sortez !
Entrez :)
Information
nil est une valeur par défaut de plusieurs autres types de variables que nous
découvrirons dans d‘autres chapitres
La dernière partie avec le mot-clé else (qui se traduit en français par "autre") traite le
cas où aucune des conditions n'a été remplie (elle est optionnelle).
Dans notre ancien exemple nous avons utilisé l'opérateur de comparaison <. En effet il
existe d'autres types d'opérateurs que je vais décrire à travers le tableau ci-dessous
Exempl
Opérateur Description e Résultat
Attention
Les opérateurs logiques vont nous permettre de vérifier si plusieurs conditions sont
bonnes. Il existe trois types d'opérateurs logiques :
Suite du scénario : "Le videur a laissé sa place à un nouveau videur très fainéant
aussi (on ne change pas une équipe qui gagne 😉) mais bon il est un peu spécial ..."
Tant mieux car au moins avec lui on va pouvoir utiliser les opérateurs logiques vu
précédemment !
Le nouveau videur n'aime pas le prénom Hatim ou hatim (je ne me sens pas visé 😢)
Le nouveau videur a tendance à changer d'humeur de temps en temps et quand il n'est
pas content il décide alors de virer les personnes ayant 18 ans
Les opérateurs de comparaison ne vont pas nous suffire, il nous faut un moyen pour
tester d'autres conditions quand la première condition n'est pas vrai. Ce cas est
gérable avec le mot-clé else if (qui se traduit en français par "sinon si"). La syntaxe
ressemble à celle du mot-clé if.
Dans cet exemple je vais utiliser la bibliothèque math/rand pour apporter de l'aléatoire
à notre code
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"time"
)
func main() {
scanner := [Link]([Link])
[Link]()
if err != nil {
[Link](2)
[Link]()
prenom := [Link]()
/*
*/
[Link]([Link]().UnixNano())
radomInt2 := [Link](2)
if age < 18 {
[Link]("Sortez !")
} else if radomInt2 == 0 {
} else {
[Link]("Entrez :)")
}
}
Copier
Résultat :
Entrez votre age : 16
Sortez !
switch, case
Une instruction switch permet de tester l'égalité d'une variable par rapport à une liste
de valeurs (idéal pour le choix dans un menu !).
package main
import (
"bufio"
"fmt"
"os"
"strconv"
func main() {
scanner := [Link]([Link])
[Link]()
if err != nil {
[Link](2)
case 0, 1: // 1 ou 0
case 23:
case 42:
case 666:
default:
Copier
Résultat :
Votre choix : 666
Votre choix : 7
William Van de Walle!
Votre choix : 1
George Boole !
Votre choix : 0
George Boole !
Votre choix : 50
Mauvais choix !
Voici un autre exemple qui nous permet de savoir si on est en week-end ou pas, dans
cet exemple nous utiliserons les fonctions Now().Weekday() de la bibliothèque time :
package main
import (
"fmt"
"time"
func main() {
switch [Link]().Weekday() {
case [Link]:
default:
Copier
Sur d'autres langages de programmation il n'est pas toujours toléré d'utiliser des
opérateurs logiques ou des opérateurs de conditions dans votre instruction switch.
Sur Go s'est toléré, mais dans ce cas il ne faut pas rajouter votre variable après votre
mot-clé switch (d'ailleurs vous être libre de vérifier n'importe quoi vu que le switch ne
se base plus sur une variable).
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"time"
)
func main() {
scanner := [Link]([Link])
[Link]()
if err != nil {
[Link](2)
switch {
println("Triste année")
println("Dimanche !")
default:
Copier
Ah un 2000 !
Triste année
Votre choix : 0
Mauvais choix !
Les boucles dans le langage
de programmation Go
Ce chapitre vous explique les boucles en GoLang. Les
boucles permettent d'itérer un bloc de code plusieurs fois. On
apprendra à utiliser la boucle for avec les mots-clés break et
continue dans le langage de programmation Go.
Description
Une boucle est un moyen d'exécuter un bloc de code plusieurs fois, suivant le
résultat d’une condition ou selon un nombre d'itérations connu à l'avance.
Information
Boucle for
Pour déclarer une boucle il suffit d'utiliser le mot-clé for.
Il existe deux types de boucle for, soit vous connaissez au préalable le nombre
d'itérations (par exemple vous savez dès le début qu'un bloc de code va s'exécuter x
fois) ou inversement vous n'avez aucune idée du nombre d'itérations de votre bloc de
code.
Copier
Imaginez le scénario suivant : "Vous êtes jeune, fainéant et mauvais élève (pas bien !).
Votre professeur vous demande alors d'écrire 100 fois la phrase suivante "Je ne dois
frapper mes camarades de classe". ça tombe vous êtes en train de suivre un cours sur
les boucles 😉 !"
package main
import (
"fmt"
func main() {
Copier
Résultat :
1 ) Je ne dois taper mes camarades de classe
...
Voici comment on déclare une boucle quand vous ne connaissez pas à l'avance le
nombre d'itérations :
for condition {
Copier
Si la condition est vraie alors on reste dans la boucle sinon on quitte la boucle.
Imaginez le scénario suivant : "Un videur de boîte de nuit refuse l'entrée des mineurs"
package main
import (
"bufio"
"fmt"
"os"
"strconv"
func main() {
scanner := [Link]([Link])
[Link]()
age, _ = [Link]([Link]())
}
Copier
Résultat :
Entrez votre age : 17
break et continue
Pour manipuler vos boucles infinies il peut être intéressant d'utiliser les deux mots-clés
suivants :
Imaginons le scénario suivant : "Vous avez bien envie de jouer à un jeu hasard mais le
problème c'est que vous n'avez pas assez d'argent 😢 par contre vous avez votre
compilateur go 😉."
Vous n'avez pas d'argent ? Pas de panique les boucles infinies vont vous aider à sortir
de cette crise !
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"time"
func main() {
[Link]([Link]().UnixNano())
randomInt := [Link](10)
scanner := [Link]([Link])
max := 20
[Link]()
if err != nil {
[Link]("Entrez un nombre !")
continue
} else {
[Link]("Dommage !")
Copier
Résultat :
Entrez votre nombre : 50
Dommage !
Dommage !
Bien joué !
Les fonctions dans le
langage de programmation
Go
Ce chapitre vous explique les fonctions en GoLang, On
apprendra aussi à déclarer et utiliser les différents types de
fonctions dans le langage programmation GO.
Explication
Les fonctions vont nous permettre de structurer nos programmes en un groupe
d'instructions qui effectueront un ensemble de tâches.
Elles permettent de simplifier notre code et de le rendre beaucoup plus lisible que ce
soit pour nous ou pour les autres, mais surtout (rappelez vous que vous êtes un bon
flemmard 😎) elles nous permettrons de ne pas retaper le même code plusieurs fois
d’affiler.
Information
Le nom de la fonction (non obligatoire si votre fonction est anonyme, on verra les
fonctions anonymes à la fin de ce chapitre) : C'est le nom qui décrit votre fonction, il
faut juste penser à respecter les mêmes règles que pour les variables (pas d'accents,
pas d'espaces, etc.). GoLang vous recommande de nommée vos fonctions en Camel
case, c'est à dire que chaque mot commence par une majuscule à l’exception du
premier.
Le type de retour de la fonction (non obligatoire si votre fonction ne retourne rien) :
comme les variables les fonctions ont un type, plus précisément c'est le type de la
valeur qu'elle retourne.
Des paramètres (non obligatoire) : Ce sont des variables que la fonction va exploiter
dans son bloc de code.
/* votre code */
Copier
Avertissement
Une fonction non anonyme doit être déclarée en dehors de la fonction main()
Voici comment on déclare une fonction sans type de retour et sans aucun paramètre.
package main
import (
"fmt"
)
// déclaration de la fonction affichage()
func affichage() {
[Link]("#################################")
[Link]("\tBonjour")
[Link]("#################################")
func main() {
Copier
Résultat :
#################################
Bonjour
#################################
Une fonction est capable de prendre autant de paramètres que vous voulez et peu
importe leurs types.
package main
import (
"fmt"
func main() {
affichage("Hatim", 9)
affichage("Alex", 12)
Copier
Résultat :
Bonjour Hatim vous avez 9 ans
On utilisera le mot-clé return pour renvoyer une valeur depuis notre fonction. Vous
pouvez ensuite stocker la valeur retournée par votre fonction dans une variable.
package main
import "fmt"
if a > b {
func main() {
[Link](max)
Copier
Résultat :
30
package main
import (
"fmt"
func main() {
a := 5
b := 8
return a + 3, b + 3
Copier
Résultat :
Avant fonction a = 5 b = 8
Après fonction a = 8 b = 11
Pour ce faire il faut rajouter des trois points ... collés avant le type du paramètre.
Exemple :
package main
import (
"fmt"
func main() {
total := 0
total += value
return total
Copier
Résultat :
91
Une fonction anonyme
Les fonctions anonymes sont utilisées lorsque nous voulons définir une fonction sans
lui attribuer de nom , elles peuvent être déclarées et appelées directement depuis
n'importe bloc de votre code enfin elles peuvent aussi être utilisées en tant que
paramètres.
Je vais dans cet exemple déclarer et utiliser une fonction anonyme en tant que
paramètre.
package main
import (
"fmt"
"math"
result := operation + 10
[Link](result)
}
func main() {
rajouterDix(9, racineCarree)
/*
*/
Copier
Résultat :
13
35
La portée des variables dans
le langage de programmation
Go
Ce chapitre vous explique les portée des variables en GoLang.
Vous allez apprendre à différencier les différents types de
portée à savoir : les variables locales, variables globales et
paramètres formels.
Présentation
Quand on parle de portée des variables on parle d’endroits dans notre code où on
peut utiliser telle ou telle variable.
"Est-ce que ma variable est accessible dans tel ou tel bloc de mon code ?"
Variables locales
Variables globales
Paramètres formels
Variables locales
Les variables déclarées à l'intérieur d'une fonction ou d'un bloc (un bloc est tout
simplement la partie de votre code dans des accolades) sont appelées variables
locales. Elles ne peuvent être accessibles qu’à l’intérieur de votre fonction ou d'un
bloc de code.
package main
import (
"fmt"
func test() {
a := 20
a *= i
[Link](a)
func main() {
test()
}
Copier
Résultat :
20
40
package main
import (
"fmt"
func test() {
a := 20
a *= i
[Link](a)
}
func main() {
test()
Copier
Erreur :
undefined: a
Si on souhaite exploiter notre variable à la fois dans notre fonction et à la fois dans
notre bloc alors il suffit de la déclarer au début de notre fonction, comme ceci
package main
import (
"fmt"
func test() {
a *= i
func main() {
test()
Copier
Résultat :
dans ma boucle for : 20
package main
import (
"fmt"
func test() {
a := 10
a += 20
[Link](a)
func main() {
test()
Copier
Erreur :
undefined: a
Variables globales
Les variables globales sont définies en dehors de vos fonctions (généralement au
début de votre programme). À l'inverse des variables locales elles conservent leur
valeur pendant toute la durée de vie du programme et sont accessibles à l’intérieur de
n’importe quelles fonctions définies dans votre programme.
package main
import (
"fmt"
func test() {
g += 20
func main() {
test()
g += 30
Copier
Résultat :
Avant l'utilisation de la fonction test() : 0
Pendant ma fonction test() : 20
Avertissement
Paramètres formels
Les paramètres formels sont traités comme des variables locales dans une fonction
par contre ils auront toujours une priorité sur les variables globales.
package main
import (
"fmt"
func main() {
test(20)
g += 30
Copier
Résultat :
Avant l'utilisation de la fonction test() : 0
...
Copier
La réponse est NON. Ça sera long et fastidieux, rappelez vous aussi que vous êtes un
bon flemmard. Donc impossible pour vous de passer votre temps à déclarer plusieurs
variables !
La solution dans ce cas c'est de créer un tableau de type int. Un tableau (array en
anglais) est une structure de données qui permet de stocker une collection
séquentielle de taille fixe de variables du même type.
Dans ce chapitre nous allons voir que des tableaux statiques, c'est-à-dire des
tableaux dont la taille est connue pendant la déclaration, nous verrons les tableaux
dynamiques dans un chapitre dédié aux Slice().
Copier
Où taille est un nombre entier qui correspond au nombre de variables que le tableau
est capable de stocker suivit du type de variable qu'il peut supporter.
Revenons à notre exemple où il fallait déclarer 100 variables de type int, en tableau
statique ça donnera quelque chose comme ça :
Copier
On peut déclarer n'importe quel type de tableau (int, float64 etc ...) et les variables
dans votre tableau auront la même valeur par défaut que d'habitude.
Exemple :
package main
import (
"fmt"
)
func main() {
Copier
Résultat :
Valeur par défaut de la variable tableauBool : [false false false false false false
false false false false]
Vous pouvez aussi surcharger les valeurs par défaut de votre tableau grâce à des
accolades.
package main
import (
"fmt"
func main() {
Copier
Information
Résultat :
les variables dans votre tableau sont accessibles depuis leur index (numéro de la
case).
Attention
package main
import (
"fmt"
func main() {
Copier
Résultat :
jours[0] = lundi
jours[1] = mardi
jours[2] = mercredi
jours[3] = jeudi
jours[4] = vendredi
jours[5] = samedi
jours[6] = dimanche
package main
import (
"fmt"
)
func main() {
Copier
Erreur :
Sauf que ça ne fonctionne pas car votre compilateur vous explique que vous être hors
limites de votre tableau, l'index que vous avez rentré est non valide. Rappelez-vous
que le premier index est toujours égal à 0, il faut donc prendre en considération le 0
quand vous calculez la taille de votre tableau, ici la valeur du dernier index est égale à
6 et non à 7.
package main
import (
"fmt"
)
func main() {
Copier
Résultat :
Vous pouvez aussi parcourir la totalité de vos variables grâce à la boucle for en
utilisant le mot-clé range.
package main
import (
"fmt"
)
func main() {
Copier
Résultat :
Il est concevable aussi d'utiliser une boucle for classique en récupérant la taille du
tableau avec la fonction len()
package main
import (
"fmt"
func main() {
Copier
Résultat :
package main
import (
"fmt"
func main() {
}
Copier
Résultat :
package main
import (
"fmt"
func main() {
Copier
Résultat :
package main
import (
"fmt"
func main() {
[Link](jours[3:])
Copier
Résultat :
package main
import (
"fmt"
func main() {
var jours = [7]string{"lundi", "mardi", "mercredi", "jeudi",
"vendredi", "samedi", "dimanche"}
[Link](jours[1:3])
Copier
Résultat :
[mardi mercredi]
Exemple :
package main
import "fmt"
func main() {
[Link](jours)
jours = replaceByHatim(jours)
[Link](jours)
/*
*/
if jour == "Inconnu" {
return jours
}
Copier
Résultat :
Un tableau à deux dimensions n'est rien d'autre qu'un tableau qui contient d'autres
tableaux (des tableaux dans un tableau).
lignes : correspond au nombre des sous tableaux inclus dans le tableau principal.
colonnes: correspond au nombre d'éléments dans les sous tableaux.
Un tableau à deux dimensions peut par exemple être utilisé pour la construction d'une
map dans un jeu 2D.
« Map d'un jeu construite depuis un tableau à double dimensions »
Exemple :
package main
import "fmt"
func main() {
const (
)
var tableau [maxLigne][maxColonne]int // Création d'un tableau à double
dimension
[Link](tableau)
Copier
Résultat :
[[0 0 0 0] [0 0 0 0] [0 0 0 0]]
package main
import "fmt"
func main() {
const (
maxLigne int = 3
maxColonne int = 3
)
[Link](doubleTableau)
[Link]("----------------------")
[Link](doubleTableau)
chaine := "Hello"
[Link](chaine)
Copier
Résultat :
[[0 0 0] [0 0 0] [0 0 0]]
----------------------
[[0 0 0] [0 0 0] [0 5 0]]
TP Le jeu du morpions dans
le langage de programmation
Go
Dans ce chapitre vous allez commencer votre premier TP sur
le langage de programmation GO en créant un jeu de
morpions en GoLang.
Présentation du TP
Il est temps de pratiquer un peu !
Nous allons créer un jeu de morpion appelé aussi le tic tac toe avec le langage de
programmation Go.
Libre à vous de choisir le design que vous souhaitez, en ce qui me concerne voici à
quoi ressemble mon jeu :
1 2 3
4 5 6
7 8 9
X 2 3
4 5 6
7 8 9
X O 3
4 5 6
7 8 9
X O 3
4 X 6
7 8 9
X O 3
4 X O
7 8 9
Information
Ici les chiffres correspondent à des cases libres. Ça permet à l'utilisateur de savoir si
sa case est libre. Libre à vous bien sûr de mettre autre chose à la place.
1 2 3
4 5 6
7 8 9
X 2 3
4 5 6
7 8 9
- Si il y a match nul :
1 2 3
4 5 6
7 8 9
X 2 3
4 5 6
7 8 9
X 2 3
O 5 6
7 8 9
Joueur 1 entrez un nombre compris entre 1 à 9 : 7
X 2 3
O 5 6
X 8 9
X 2 3
O O 6
X 8 9
X X 3
O O 6
X 8 9
X X O
O O 6
X 8 9
X X O
O O X
X 8 9
X X O
O O X
X 8 O
X X O
O O X
X X O
Partie nulle !
Sachez que vous possédez les connaissances nécessaires vues dans les chapitres
précédents, et donc vous êtes tout à fait capables de réaliser ce jeu.
Solution
J'espère que vous avez réussi à réaliser ce tp ! Même si vous n'avez pas forcément
réussi à tout faire. L'essentiel c'est qu'au moins quelques fonctionnalités du jeu
fonctionnent, ça restera toujours mieux que de voir la solution directement sans même
essayer.
package main
import (
"bufio"
"fmt"
"os"
"strconv"
// constantes globales
const (
tailleDamier = 9
symboleJoueur1 = "X"
symboleJoueur2 = "O"
// Variables globales
var (
tableauMorpion = [tailleDamier]string{ // création d'un damier
sans aucune cases remplies
func main() {
/**
* @return rien
*/
func jouer() {
affichage()
remplirCase(numeroCase)
affichage()
affichage()
/**
* @return rien
*/
func affichage() {
[Link]()
/**
* @return string
*/
if joueur1 {
return "Joueur 1 "
} else {
/**
*/
var (
numeroCase = 0
err error
scanner = [Link]([Link])
)
for bonneEntree == false {
[Link]()
} else {
bonneEntree = true
}
/**
* @param numeroCase
* @return rien
*/
if joueur1 {
tableauMorpion[numeroCase] = symboleJoueur1
} else {
tableauMorpion[numeroCase] = symboleJoueur2
/**
* @return bool
*/
/*
*/
tableauxdeGain := [][tailleDamier]bool{
tableauMorpionBool[index] = true
tableauMorpionBool[index] = true
ressemblance++
return false
/*
* @return bool
*/
occurence := 0
/*
*/
}
Les pointeurs dans le
langage de programmation
Go
Ce chapitre vous explique les pointeurs en GoLang. Vous allez
apprendre d'abord le fonctionnement de la mémoire de votre
ordinateur et ensuite découvrir comment déclarer, accéder et
modifier des pointeurs dans le langage de programmation GO.
Le fonctionnement de mémoire
Fonctionnement
Avant de vous présenter le concept des pointeurs, une petite explication à propos de
la mémoire s’exige.
Vous ne vous êtes jamais demandé comment votre machine manipule les valeurs de
vos variables ?
Oui tout à fait, toute variable manipulée dans un programme est stockée quelque part
dans la mémoire de votre machine. Cette mémoire est constituée d'octets qui sont
identifiés de manière univoque par un numéro qu'on appelle adresse Donc si votre
machine veut manipuler vos variables, elle aura besoin au préalable de connaître où
se situe votre variable grâce à son adresse mémoire qui lui indiquera la position de la
donnée dans la mémoire vive (la RAM de votre ordinateur).
Par exemple si on prend le fonctionnement de la poste, le facteur a besoin de
l'adresse de destination pour savoir chez qui il doit renvoyer la lettre. Ici le facteur c'est
votre ordinateur et l'adresse destination correspond à l'adresse de la variable que
l'ordinateur souhaite chercher.
package main
import (
"fmt"
func main() {
var a int = 20
Copier
Résultat :
Adresse de la variable a: 0xc0000100a0
Schéma explicatif
Je vous partage ici un code simple qui affiche et modifie une variable et je vais ensuite
vous expliquer comment votre machine raisonne quand elle exécute votre code.
package main
import (
"fmt"
func main() {
var a int = 20
var b int = 40
[Link](a)
b = 50
Copier
Maintenant que vous avez compris qu’est-ce une adresse mémoire et comment y
accéder, alors je peux passer à la suite en vous expliquant les pointeurs !
Les pointeurs sont des variables dont le contenu est une adresse mémoire d'une autre
variable, c'est-à-dire l'adresse directe de l'emplacement mémoire d'une variable.
Vous devez déclarer un pointeur avant de pouvoir l'utiliser pour stocker l'adresse
d'une variable. La forme générale d'une déclaration de variable de pointeur est :
Copier
Après la théorie, passant à un peu de pratique. Pour cet exemple je vais déclarer un
pointeur et une variable et accéder directement à la valeur de ma variable depuis son
adresse grâce à mon pointeur.
package main
import "fmt"
func main() {
var a int = 20
Copier
Résultat :
Adresse de votre variable: 0xc000056058
Même si j'ai mis quelques commentaires dans le code, une petite explication du code
s’impose :
var a int = 20
Copier
Copier
ap = &a
Copier
Petit rappel "un pointeur ne peut stocker que des adresses mémoire". Ça tombe bien
car ici grâce au signe & on peut accéder à l'adresse mémoire de notre variable a
Copier
Copier
Sur cette étape j'affiche la valeur du pointeur ap pour vous démontrer qu'il possède bel
et bien comme valeur l’adresse de votre variable a
[Link]("Valeur de l'adresse %p: %d\n", ap, *ap)
Copier
Pour cette étape j’accède directement à la valeur de la variable a depuis son adresse
grâce au pointeur ap.
L'astérisque * nous permet entre autres de déclarer un pointeur mais il permet aussi
d'accéder à la valeur de la variable pointée, sur cette ligne de code on l'utilise pour
accéder à la valeur contenue dans l'adresse mémoire de la variable a
La valeur par défaut d'un pointeur est nil. Ça peut vous aider pour savoir si votre
pointeur comporte une adresse mémoire ou pas, exemple :
package main
import "fmt"
func main() {
} else {
var b int = 20
var pb *int
pb = &b
if pb == nil {
} else {
Copier
Résultat :
Aucune adresse mémoire
Comme on peut accéder à l'adresse mémoire d'une variable depuis un pointeur alors il
est tout à fait réalisable de modifier la valeur qui se trouve dans cette adresse
mémoire. Voyons voir cela de plus près :
package main
import "fmt"
func main() {
var a int = 20
var ap *int
ap = &a
Copier
Résultat :
Valeur de la variable a : 20
Valeur de la variable a : 30
*ap = 30
Copier
Il est concevable de déclarer des pointeurs en tant que paramètres dans une fonction.
Attention
Il faut savoir qu'un changement de valeurs d'une variable actionné depuis un pointeur
est irréversible car le pointeur va directement modifier la valeur depuis votre adresse
mémoire.
package main
import "fmt"
func main() {
var a int = 20
rajouterCinq(&a)
affichage(&a)
Copier
Résultat :
Avant de modifier la variable de a : 20
J'ai modifié la valeur de ma variable a depuis le pointeur défini en tant que paramètre
sur la fonction rajouterCinq(), comme je l'ai expliqué plus haut, les changements
sont irréversibles peu importe l'endroit où je l'affiche (fonction main() ou autres).
Conclusion
Les pointeurs sont des fonctionnalités de bas niveau, il était donc nécessaire de vous
expliquer le fonctionnement de la mémoire d’un ordinateur pour que vous puissiez
ensuite bien comprendre comment les utiliser correctement.
Sur le chapitre qui traitait sur la portée des variables, je vous avais expliqué qu’il n'était
pas possible de modifier une variable locale hors de sa fonction ou de son bloc mais
avec les pointeurs il devient possible de modifier une variable locale même si cette
dernière est déclarée dans une autre fonction ou dans un autre fichier (package) et
c’est vraiment là toute la puissance des pointeurs. On peut déclarer notre variable
n’importe où dans notre code et la modifier sur n'importe quel l’endroit de notre code.
Vous aurez le plaisir d’utiliser les pointeurs quand vous travaillerez sur des plus gros
projets, pour le moment mon but était juste de vous faire comprendre la notion et
l’utilisation basique des pointeurs mais sachez qu'on les utilisera sur d'autres chapitres
comme le chapitre sur les structures.
Si vous avez des questions n’hésitez pas à me les poser sur mon LinkedIn (je suis très
actif dessus !) 😊.
package main
import "fmt"
func main() {
[Link](tableau)
tableau[1] = "text"
Copier
Erreur :
Pour résoudre ce problème on peut entre autres utiliser les structures. Une structure
est tout simplement un type de données disponibles sur Go qui est défini par
l'utilisateur et qui vous permet de combiner des éléments de données de différents
types.
nom_var_1 type;
nom_var_2 type;
...
nom_var_3 type;
Copier
Vous pouvez ensuite utiliser votre structure comme un type variable, exemple :
package main
import "fmt"
func main() {
nom string
age int
[Link](perso)
}
Copier
Résultat :
{ 0}
package main
import "fmt"
func main() {
nom string
age int
[Link](perso)
}
Copier
Résultat :
{Hatim 20}
Comme résultat nous avons les valeurs par défaut des attributs contenues dans la
structure Personnage
package main
import "fmt"
func main() {
nom string
age int
Copier
Résultat :
{Hatim 20]
la vie du personnage
la puissance du personnage
le nom du personnage
Savoir si le personnage est mort ou pas
L'inventaire du personnage
Pour assouvir votre besoin on va stocker ces variables dans une structures
nommée Personnage
package main
import "fmt"
nom string
vie int
puissance int
mort bool
inventaire [3]string
func main() {
var p1 Personnage // initialisation de ma structure Personnage
/*
*/
[Link] = "magix"
[Link] = 100
[Link] = 20
[Link] = false
if [Link] {
} else {
}
[Link]("\nLe personnage", [Link], "possède dans son inventaire
:", [Link])
[Link]("-", item)
Copier
Résultat :
Vie du personnage magix : 100
- potion
- bâton
- poison
Structures et pointeurs
Il est possible d'utiliser les pointeurs sur des structures pour modifier directement les
valeurs des attributs de notre structure dans une fonction autre que la fonction main().
On va reprendre l'exemple précédent et déclarer une fonction qui permet de
surcharger les valeurs par défaut de votre personnage en combinant structures et
pointeurs.
package main
import "fmt"
nom string
vie int
puissance int
mort bool
inventaire [3]string
func main() {
var p1 Personnage
valeurParDefaut(&p1)
[Link]("Vie du personnage", [Link], ":", [Link])
if [Link] {
} else {
[Link]("-", item)
/*
*/
[Link] = "inconnu"
[Link] = 50
[Link] = 10
[Link] = false
Copier
Résultat :
Vie du personnage inconnu : 50
- vide
- vide
- vide
Information
Pas besoin d'utiliser l'astérisque * pour accéder ou modifier une valeur de la structure
pointée.
Les méthodes
Une méthode n'est rien d'autre que le nom qu'on donne à une fonction avec un
récepteur défini (ici le récepteur est notre structure). Pour faire simple les méthodes
sont littéralement des fonctions liées à votre structure c'est-à-dire que ce sont des
fonctions qui ne peuvent être appelées que par votre structure.
Je vais dans cet exemple déclarer une méthode nommée affichage() liée à la
structure Personnage :
package main
import (
"fmt"
nom string
vie int
puissance int
mort bool
inventaire [3]string
}
func main() {
var p1 Personnage
var p2 Personnage
valeurParDefaut(&p1)
valeurParDefaut(&p2)
[Link] = "barbare"
[Link] = "magicien"
[Link]()
[Link]()
if [Link] {
} else {
[Link]("-", item)
/*
*/
[Link] = "inconnu"
[Link] = 50
[Link] = 10
[Link] = false
Copier
Résultat :
--------------------------------------------------
- vide
- vide
- vide
--------------------------------------------------
- vide
- vide
- vide
Méthodes et pointeurs
On va combiner les pointeurs avec les méthodes 💥.
package main
import (
"fmt"
nom string
vie int
puissance int
mort bool
inventaire [3]string
func main() {
var p1 Personnage
var p2 Personnage
[Link]()
[Link]()
/*
@return: void
*/
func (p *Personnage) Init(nom string, vie int, puissance int, mort bool,
inventaire [3]string) {
[Link] = nom
[Link] = vie
[Link] = puissance
[Link] = mort
[Link] = inventaire
/*
@return: void
*/
[Link]("--------------------------------------------------")
if [Link] {
} else {
}
[Link]("\nLe personnage", [Link], "possède dans son
inventaire :", [Link])
[Link]("-", item)
Copier
Résultat :
--------------------------------------------------
- épée
- bouclier
- armure
--------------------------------------------------
- potions
- poisons
- bâton
Conclusion
Go n'est pas un langage de programmation purement orienté objet. Les structures et
les méthodes sont concepts qui peuvent être implémentés à l'aide de Go et qui se
rapprochent de la POO (programmation orientée objet).
Mon but dans ce chapitre est de vous montrer que les structures comme son nom
l'indique permettent de mieux structurer notre code.
Je vous conseille de reprendre mon exemple sur les personnages et de rajouter des
attributs et des méthodes (Attaquer(), Soigner(), etc …) sur votre structure
Personnage et pourquoi pas recréer le tp précédent à base de structures.
Les Slices (tableaux
dynamiques) dans le langage
de programmation Go
Ce chapitre vous explique les Slices en GoLang. Elles
permettent de créer des tableaux dynamiques. Vous allez
apprendre à déclarer et à copier une Slice mais aussi à
récupérer, rajouter, modifier et supprimer les éléments d'une
Slice dans le langage de programmation Go.
Et c’est là qu’interviennent les Slices dans Go, Ils vont nous permettre d’avoir
un tableau flexible et le dimensionner de façon dynamique sans se soucier de sa
taille pendant sa déclaration.
package main
import (
"fmt"
func main() {
[Link](nombres)
Copier
Résultat :
[0 0 0 0 0]
package main
import (
"fmt"
)
func main() {
[Link](nombres)
Copier
Résultat :
[0 0 0 0 0]
package main
import "fmt"
func main() {
[Link](mois)
[Link](mois)
Copier
Résultat :
[Janvier]
[Janvier Février]
Avant de vous montrer comment faire pour supprimer n'importe quel élément d'une
slice, laissez-moi avant vous expliquer comment fonctionne la fonction append().
Copier
La fonction append() prend comme premier argument une Slice, le second paramètre
est quant à lui est un paramètre variadique c'est-à-dire qu'il prend en compte un
nombre indéterminé de paramètres.
package main
import "fmt"
func main() {
[Link](mois)
indexASupprimer := 1
[Link](mois)
Copier
Résultat :
[Janvier Février Mars Avril Juin Juillet]
Attention
La fonction copy() copie le contenu d’une tranche source vers une source cible, il
est donc important que la slice cible soit de même taille que la slice source afin de
copier le contenu total de la cible.
package main
import "fmt"
func main() {
animaux1 := []string{"Lion", "Cheval", "Ours"}
copy(animaux2, animaux1)
Copier
Résultat :
Contenu du tableau animaux1 : [Lion Cheval Ours]
Avant pour récupérer un élément précis dans votre tableau vous utilisiez un index, les
Maps possèdent le même processus de récupération d’éléments qu’un tableau
cependant pour les Maps votre index est nommé clé et votre clé peut être de n’importe
quel type (int, string, float64 …) à l'instar des tableaux où l’index est obligatoirement de
type int.
Par exemple on peut créer une Map avec comme clé les noms des élèves et comme
valeur leurs notes et intercepter la note d’un élève précis (la valeur) en utilisant son
nom (la clé)
Déclarer une Map
Comme les Slices, il existe deux façons pour créer une Map.
Première méthode :
package main
import (
"fmt"
func main() {
Copier
package main
import (
"fmt"
)
func main() {
Copier
Comme dans d'autres types de variables il est aussi possible de surcharger les valeurs
par défaut de votre Map.
package main
import "fmt"
func main() {
[Link](notes)
Copier
Résultat :
map[Alex:18 Hatim:20]
Rajouter un élément dans une Map
Voici comment on rajoute un élément dans une Map.
package main
import "fmt"
func main() {
notes["Hatim"] = 20
notes["Alex"] = 18
notes["Kevin"] = 15
[Link](notes)
Copier
Résultat :
map[Alex:18 Hatim:20 Kevin:15]
Récupérer les éléments d'une Map
Récupérer un élément précis
package main
import "fmt"
func main() {
Copier
Résultat :
La note de Hatim est : 20
On peut utiliser la boucle for avec le mot-clé range pour récupérer la clé de tous les
éléments de votre Map.
package main
import "fmt"
func main() {
Copier
Résultat :
La note de Hatim est 20
package main
import "fmt"
func main() {
[Link](notes)
delete(notes, "Hatim")
[Link](notes)
Copier
Résultat :
map[Alex:18 Hatim:20 Kevin:15 Robert:17]
Définition
Une interface est un ensemble de signatures de méthodes qu'une structure
peut implémenter. Par conséquent, l'interface définit le comportement d’une structure.
Le but principal de l'interface consiste à ne fournir que des signatures de
méthodes composées :
Du nom de la méthode
Des arguments d'entrée
Des types de retour
Pour déclarer une interface nous avons besoin du mot-clé type qui permet d'associer
un nom à votre interface et du du mo-clé interface qui indique à votre compilateur
qu'il s'agit d'une interface.
package main
import (
"fmt"
}
func main() {
[Link](f)
Copier
Comme vous pouvez le voir dans une interface on ne met que des signatures de
méthodes sans aucun attributs. Pour le moment ces méthodes ne sont pas encore
utilisées car pour l'instant je vous montre juste comment déclarer une interface.
Résultat :
nil
D'après le résultat ci-dessus, nous pouvons voir que la valeur de notre interface
est nil car il n'existe aucune structure qui implémente l'interface Forme.
package main
import (
"fmt"
Air() float64
Perimetre() float64
largeur float64
longueur float64
/*
*/
/*
*/
func main() {
var f Forme
r := Rectangle{5.0, 4.0}
[Link]("Type de f :", f)
[Link]("Valeur de f : %v\n", f)
[Link]("f == r ? ", f == r)
}
Copier
Résultat :
Type de f : {5 4}
Valeur de f : {5 4}
Air du rectangle r : 20
f == r ? true
Sur le résultat on peut remarquer que f == r ? est à true car dans mon
exemple f et r ont le même type et la même valeur.
package main
import (
"fmt"
)
type Forme interface {
Air() float64
Perimetre() float64
largeur float64
longueur float64
func main() {
var f Forme
f = Rectangle{5.0, 4.0}
r := Rectangle{5.0, 4.0}
[Link]("Type de f :", f)
[Link]("Valeur de f : %v\n", f)
[Link]("f == r ? ", f == r)
Copier
Erreur :
.cannot use Rectangle literal (type Rectangle) as type Forme in
assignment:
L'erreur ci-dessus indique clairement que, pour implémenter une interface avec
succès, vous devez implémenter toutes les méthodes déclarées par l'interface et notre
compilateur nous le fait bien savoir on nous marquons qu'il manque la
méthode Air() "missing Air method".
On va dans cet exemple créer deux structures Cercle et Rectangle qui implémente
l'interface Forme :
package main
import (
"fmt"
"math"
Air() float64
Perimetre() float64
largeur float64
longueur float64
rayon float64
func main() {
[Link]("Cercle :")
AirPerimetrePresentation(r)
[Link]("\nrectangle :")
AirPerimetrePresentation(c)
Copier
Résultat :
Cercle :
- Air : 20
- Perimetre : 40
rectangle :
- Air : 78.53981633974483
- Perimetre : 31.41592653589793
Le résultat ci-dessus nous montre que l'interface Forme peut être déclaré en tant
que Cercle ou Rectangle
Conclusion
Les interfaces sont une sorte de contrat, ils permettent de créer des comportements
génériques: si plusieurs structures doivent obéir à des comportements particuliers,
alors on créé une interface décrivant ces comportements. Ces structures devront ainsi
obéir strictement aux signatures de l'interface, sans quoi la compilation ne se fera pas.
Grâce à eux on peut obtenir un code beaucoup plus claire et facile à maintenir.
Gestion des erreurs dans le
langage de programmation
Go
Ce chapitre vous montre comment gérer vos erreurs en
GoLang, vous allez apprendre à détecter, créer et gérer vos
erreurs dans le langage de programmation GO.
Présentation
Il est possible avec le langage de programmation go de gérer les erreurs plus
précisément Go nous permet de détecter ou de créer une erreur pour ensuite la
manier comme bon nous semble.
package main
import (
"bufio"
"fmt"
"os"
"strconv"
func division() {
scanner := [Link]([Link])
[Link]()
nbr, _ := [Link]([Link]())
}
func main() {
division()
[Link]("Fin")
Copier
Erreur :
Entrez un chiffre : nope
Sans aucune surprise nous obtenons une erreur qui nous explique qu'il est impossible
de diviser par 0.
Pour le bon déroulement de notre programme, il faut gérer cette erreur en expliquant à
l'utilisateur qu'il n'est pas possible de diviser par 0 et qu'il doit rentrer un nombre
supérieur à 0.
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func division() {
for true {
scanner := [Link]([Link])
[Link]()
nbr, _ := [Link]([Link]())
if nbr <= 0 {
} else {
break
func main() {
division()
[Link]("Fin")
Copier
Résultat :
Entrez un chiffre : dsfsdf
Entrez un chiffre : 78
Résultat : 12
Fin
C'est cool car on a résolu le problème de la division par zéro mais par contre lorsque
l'utilisateur rentre des caractères on lui affiche toujours le même message comme quoi
son entrée doit être supérieur à 0. Il serait plus judicieux de l'informer qu'il doit rentrer
un nombre et non des caractères.
Pour afficher cette information on va analyser un peu plus en détail la fonction Atoi().
Déjà une chose est sûr l'erreur est levée lors de la fonction Atoi(), voyons voir un
peu plus en détail le prototype de cette fonction :
Copier
On peut apprendre grâce au prototype que la fonction Atoi() retourne deux types de
valeurs :
Donc pour gérer cette erreur de conversion il faut vérifier la valeur de retour
de error de la fonction Atoi() dans une condition.
package main
import (
"bufio"
"fmt"
"os"
"strconv"
func division() {
for true {
scanner := [Link]([Link])
[Link]()
} else {
break
func main() {
division()
[Link]("Fin")
Copier
Résultat :
Entre un chiffre : -5
[division par zéro impossible] Votre valeur doit être supérieur ou
égal à 0
Entre un chiffre : 5
Résultat : 200
Fin
package main
import (
"errors"
"fmt"
"os"
)
func verificationDivision(nbr float64) (float64, error) {
if nbr <= 0 {
} else {
func main() {
nbr := 0.0
if err != nil {
panic(err)
} else {
[Link]("Aucune erreur")
}
Copier
Résultat :
panic: Erreur: Il est impossible de diviser par 0 !
goroutine 1 [running]:
exit status 2
Information
Si votre fonction retourne une interface error alors vous ne pouvez retourner que le
type nil ou le type errors
Ici j'utilise la fonction panic() pour quitter mon programme avec un code
retour différent de 0. Un code d'erreur égale à zéro indique que l'exécution de votre
programme s'est bien déroulée si il est différent de zéro cela indique un échec de
votre programme. C'est une valeur qui est retournée lors de la fin d'exécution de votre
programme.
Sur Linux il est possible d'afficher le dernier code d'erreur d'un programme/commande
en utilisant la commande suivante :
echo $?
Copier
package main
import (
"fmt"
if nbr <= 0 {
return 0, false
} else {
func main() {
nbr := 0.0
nbr, err := verificationDivision(nbr)
if err == false {
} else {
[Link]("Aucune erreur")
Copier
Mais il reste préférable de gérer vos erreurs en utilisant la fonction [Link]() afin
de rendre votre code plus lisible et clair. Cela aide à la compréhension, à la relecture,
au contrôle visuel et à la maintenance de votre code.
Les packages dans le
langage de programmation
Go
Ce chapitre vous explique les packages en GoLang. Ils
permettent de déplacer des portions de votre code dans
d’autres fichiers afin de mieux organiser et maintenir votre
programme. Vous allez apprendre à créer et gérer des
packages dans le langage de programmation Go.
Définition
Jusqu'ici nous n'avons écrit notre code que dans un seul fichier, pour l'instant c'était
tolérable car la taille de note code était petite, mais plus votre code va grandir plus
vous allez vite vous rendre compte qu'il est nécessaire de déplacer des portions de
votre code dans d'autres fichiers afin de mieux organiser et maintenir votre
programme, et c'est là qu'interviennent les packages !
Un package n'est rien d'autre qu'un répertoire contenant des fichiers de code go, qui
peut être exécuté par d'autres fichiers go.
Comme nous en avons discuté au début de nos chapitres, il existe deux types de
packages :
Package exécutable
Package utilitaire
Création de packages
Où créer mon package ?
Pour importer un paquet, il faut utiliser le mot-clé import suivi du nom du paquet.
Lorsque vous créez un paquet et que vous l'importez sur votre package principal, votre
compilateur va d'abord chercher le répertoire du paquet dans le $GOROOT/src/ et si
répertoire n'existe pas il va le cherchera dans le dossier $GOPATH/src/.
Information
go env
Copier
Résultat :
set GOCACHE=C:\Users\hatim\AppData\Local\go-build
...
set GOPATH=C:\Users\hatim\go
set GOROOT=C:\Go
...
Je vais volontairement importer un package qui n'existe pas, pour vous prouver qu'il
cherche bel et bien vos packages dans ces deux chemins :
package main
import (
"aucunpackage"
"fmt"
func main() {
[Link]([Link]())
Copier
Erreur :
Dans cet nouvel exemple, nous allons créer un simple package qui nous permet
d'afficher un nom et un sexe, mais avant de toucher à du code on va d'abord réfléchir
à notre arborescence
Ensuite pour créer un package il faut créer un dossier avec le nom de notre package
suivi d'un fichier (même nom que le package de préférence) ou on déposera le code
Go de notre package.
Dans l'exemple qui suit notre package se nommera affichage et notre package
exécutable se nommera [Link]. Ce qui nous donnera l'arborescence suivante :
$GOPATH/src
├── affichage
| ├── [Link]
[Link]
Création du package
package affichage
Copier
Attention
La ligne de code package affichage indique à notre compilateur qu'il s'agit bien d'un
package utilitaire nommé "affichage". Ensuite pour le reste du code je déclare deux
variables et une fonction qui peuvent être réutilisées depuis le fichier [Link] une fois
qu'il aura importé le package.
Il ne reste plus maintenant qu'à importer le package affichage dans le fichier [Link]
et par la suite il sera possible depuis le fichier [Link] d'utiliser les fonctions et les
variables du package affichage.
package main
import (
"affichage"
"fmt"
)
func main() {
[Link]([Link]())
Copier
Résultat :
Je m'appelle Hatim
Imbrication de paquets
Nous pouvons imbriquer un paquet dans un paquet. Tout ce que nous avons à faire
c'est de fournir le chemin relatif du paquet imbriqué.
$GOPATH/src
├── affichage
| ├── [Link]
| ├── passion
| ├── [Link]
[Link]
Voici à quoi va ressembler notre fichier [Link] :
package passion
Copier
package main
import (
"affichage"
"fmt"
func main() {
[Link]([Link]())
[Link]([Link]())
Copier
ici on utilise le chemin relatif pour importer notre package passion imbriqué dans notre
package affichage.
Résultat :
Je m'appelle Hatim
j'aime programmé en Go !.
La programmation orientée
objet dans le langage de
programmation Go
Ce chapitre vous explique la programmation orientée objet en
GoLang. Même si GO n'est pas un langage purement orienté
objet il est possible en réussissant à combiner les structures et
les packages de se rapprocher le plus possible de la POO
dans le langage de programmation GO.
Is Go an object-oriented language?
Yes and no. Although Go has types and methods and allows an object-oriented style
of programming, there is no type hierarchy. The concept of “interface” in Go provides a
different approach that we believe is easy to use and in some ways more general.
There are also ways to embed types in other types to provide something analogous—
but not identical—to subclassing. Moreover, methods in Go are more general than in
C++ or Java: they can be defined for any sort of data, even built-in types such as plain,
“unboxed” integers. They are not restricted to structs (classes).
faq de go
En gros il nous explique qu'il n'existe pas de hiérarchie des types en Go (par exemple
la notion d'héritage). Mais qu'il existe des approches différentes de hiérarchisation de
nos classes en utilisant par exemple les packages imbriqués où le répertoire racine
sera la classe mère et les sous dossier des classes filles qui qui héritent de la classe
mere.
Pour résumé Go n'est pas un langage de programmation purement orienté objet . Mais
en combinant les structures et les packages, il est réalisable de se rapprocher le plus
possible de la POO.
la vie du personnage
la puissance du personnage
le nom du personnage
Savoir si le personnage est mort ou pas
L'inventaire du personnage
Presentation()
ViePerdu()
EstMort()
Nous allons créer notre structure Personnage dans un package personnage, ce qui
nous donnera l'arborescence suivante :
$GOPATH/src
├── personnage
| ├── [Link]
[Link]
package personnage
import (
"fmt"
Nom string
Vie int
Puissance int
Mort bool
Inventaire [3]string
/*
@return: void
*/
if [Link] {
} else {
[Link]("-", item)
Copier
Rappel
Go exporte une variable/fonctions dans un autre package si et seulement si le nom
commence par une lettre majuscule. Toutes les autres attributs/fonctions ne
commençant pas par une lettre majuscule elles sont considérées comme variables
privées ou fonctions privées par le paquet, c'est-à-dire qu'elles ne sont pas
exploitables en dehors du package où elles ont été initialisées.
package main
import (
"personnage"
func main() {
Nom: "magix",
Vie: 100,
Puissance: 20,
Mort: false,
}
[Link]()
Copier
Une fois notre package importé, on crée ensuite une instance de notre classe qu'on
nomme magicien et on lui affecte des valeurs par défaut.
--------------------------------------------------
- potions
- poisons
- bâton
Les constructeurs
Le rôle du constructeur est de déclarer et d'initialiser les attributs d'une classe. Go ne
supporte pas par défaut les constructeurs, mais il est possible de les créer en
bidouillant avec les méthodes.
Avant de vous montrer comment on créée une sorte de constructeur en Go, voici
d'abord une comparaison avec le le langage Java.
Copier
{
// Instanciation de la classe Personnage
Copier
C'est grâce à l'opérateur new, qu'on arrive à faire appel à un constructeur d'une classe
en java. Sur Go on va bidouiller un peu dans notre package personnage pour recréer
l'opérateur new utilisé dans java (mais aussi dans beaucoup d'autres langages de
programmations).
Pour ça on va créer une méthode nommée New dans package personnage, comme
suit :
package personnage
import (
"fmt"
Nom string
Vie int
Puissance int
Mort bool
Inventaire [3]string
/*
*/
func New(Nom string, Vie int, Puissance int, Mort bool, Inventaire
[3]string) Personnage {
return personnage
/*
@return: void
*/
func (p Personnage) Affichage() { // déclaration de ma méthode
Affichage() liée à ma structure Personnage
[Link]("--------------------------------------------------")
if [Link] {
} else {
[Link]("-", item)
Copier
Notre fichier [Link] va ressembler à ça :
package main
import (
"personnage"
func main() {
[Link]()
Copier
Conclusion
Vous l'aurez sans doute compris même si Go ne prend pas en charge les classes, il
reste tout de même possible d'utiliser les structures à la place des classes. Grâce à
cette fusion entre structures et packages il est possible d'encore mieux organiser
son code en s'orientant plus vers la POO. Mon but sur ce chapitre était de vous
introduire à la POO. Il est bien sûr tout à fait possible d'aller encore plus loin en créant
par exemple une notion d'héritage grâce aux interfaces, je créerai sûrement un article
à propos de ce sujet.
Suggestion d'exercice
Avant de nous quitter sur ce chapitre je vous conseil de recréer le TP sur les morpions
avec des classes ! Vous avez largement les ressources nécessaires pour réussir à
bien cet exercice.
Les goroutines dans le langage
de programmation Go
Ce chapitre vous explique les goroutines en GoLang. Elles
permettent de créer des programmes multi-threads simplement.
Vous allez d'abord comprendre la différence entre concurrence et
parallélisme et ensuite apprendre à créer et gérer vos différentes
goroutines sur le langage de programmation Go.
Concurrence et parallélisme
Avant d'aborder des goroutines il est essentiel de comprendre la différence entre concurrence
et parallélisme.
Concurrence
Un humain normal doit d'abord finir sa bouchée avant de pouvoir parler, une fois qu'il aura
fini sa bouchée il pourra parler ensuite une fois qu'il aura fini de parler il pourra encore une
fois reprendre une autre bouchée et reparler juste après.
Dans cet exemple la personne est capable capable de gérer plusieurs de choses (manger et
parler) dans un intervalle de temps différent.
Parallélisme
L'humain normal de l'exemple précèdent devient un humain mutant avec un bras et une
bouche en plus. Cette fois-ci il est capable de manger et de parler en même temps.
Le fonctionnement d'un point de vue informatique
Maintenant que vous avez compris ce qu'est la concurrence et comment elle diffère du
parallélisme en utilisant des exemples de la vie réelle, il est temps maintenant de comprendre
qu'est ce ça donne d'un point de vue technique.
Imaginons que vous codez votre propre navigateur Web et qu'un utilisateur utilise votre
navigateur pour visiter une page web afin de télécharger une vidéo. Il clique alors sur le
bouton de téléchargement de la page et en attendant la fin de son téléchargement il visite
d'autres pages du site.
Votre navigateur doit dans ce cas gérer deux taches de manière indépendante, une tâche pour
le téléchargement et une autre tache pour le rendu des pages web que l'utilisateur va visiter.
Cette tâche indépendante est ce qu'on appelle un thread.
Lorsque votre navigateur est exécuté dans un processeur avec un seul cœur (ou processeur
multicœur), le processeur bascule entre les deux tâches (chaque tâche va s'exécuter un petit
moment puis une autre, puis une autre, puis on revient à la première, etc) ceci est connu sous
le nom de la concurrence. Dans ce cas, le téléchargement et le rendu commencent à
différents moments et leurs exécutions se chevauchent.
Disons que le même navigateur est exécuté sur une autre machine avec un processeur
multicœur et que la tâche de téléchargement et la tâche de rendu HTML s'exécute
simultanément sans chevauchement dans des cœurs différents alors ceci est plus connu sous le
nom de parallélisme.
Schéma sur le fonctionnement de la concurrence et le parallélisme sur un Processeur avec un
seul cœur et deux cœurs.
Vos tâches peuvent parfois avoir besoin de communiquer entre eux. Par exemple dans votre
navigateur dès que l'utilisateur aura fini son téléchargement une popup indiquant à l'utilisateur
que le téléchargement c'est bien déroulé apparaîtra au-dessus du rendu si et seulement si la
page courante n'est pas en plein écran. Dans ce cas on parle de concurrence et l'avantage de
cette dernière c'est que les différentes tâches peuvent potentiellement accéder à des données
partagées cependant il peut y avoir un risque de décohérence entre les deux.
Les goroutines
Pourquoi les goroutines ?
L'un des aspects les plus intéressants dans Go est son modèle de concurrence, il rend la
création de programmes multi-threads simples.
Pratiquons un peu !
Pour mieux comprendre les gains de performances avec les goroutines, laissez moi vous
présenter un programme standard sans goroutines qui attend 3 secondes pour chaque appel de
la fonction run().
package main
import (
"fmt"
"time"
)
func main() {
debut := [Link]()
run("Hatim")
run("Robert")
run("Alex")
fin := [Link]()
[Link]([Link](debut))
Copier
Résultat :
Hatim : 0
Hatim : 1
Hatim : 2
Robert : 0
Robert : 1
Robert : 2
Alex : 0
Alex : 1
Alex : 2
9.0095154s
Sans grande surprise le temps d'exécution est d'un peu après 9 secondes. Maintenant essayons
d'améliorer les performances de notre programme en utilisant les goroutines.
Pour créer une goroutine il faut placer le mot clé go avant un appel de fonction, exemple :
package main
import (
"fmt"
"time"
)
func run(name string) {
[Link](1 * [Link])
func main() {
debut := [Link]()
go run("Hatim")
go run("Robert")
run("Alex")
fin := [Link]()
[Link]([Link](debut))
Copier
Avertissement
J'ai volontairement pas placé le mot clé go avant la ligne run("Alex"), vous allez
comprendre pourquoi plus tard.
Résultat :
Robert : 0
Hatim : 0
Alex : 0
Hatim : 1
Robert : 1
Alex : 1
Robert : 2
Hatim : 2
Alex : 2
3.0022266s
Hé hé, vous voyez c'est un sacré gain de temps d'exécution 😵. Il ne faut pas se réjouir trop vite
car maintenant je vais rajouter une goroutine à la ligne run("Alex").
package main
import (
"fmt"
"time"
[Link](1 * [Link])
func main() {
debut := [Link]()
go run("Hatim")
go run("Robert")
go run("Alex")
fin := [Link]()
[Link]([Link](debut))
}
Copier
Résultat :
0s
Oulala 😨, 0 seconde et aucune fonction run() qui s'exécute ?! Mais que s'est-il passé ?
Pour répondre à ces questions répondrez d'abord à celle la : "Selon vous combien de
goroutines se sont exécutés ?"
La réponse est 4 !
Il y a certes les 3 goroutines de la fonction run() mais aussi une goroutine principale, je
m'explique. Lorsqu'un programme Go démarre, une goroutine commence à s'exécuter
immédiatement, c'est la goroutine principale de votre programme, c'est celle qui est exécutée
lorsque votre programme commence à s'exécuter. C'est la goroutine à partir duquel
d'autres goroutines enfants seront générées (ici les goroutines enfants sont ceux de la
fonction run()).
Lorsque l'exécution de la goroutine principale est terminée, les goroutines enfants sont
abandonnées aussi
Ce qui s'est passé c'est que le thread principal s'est alors terminé après l'exécution de la
fonction [Link]([Link](debut)) et c'est qui a tué les goroutines enfants.
La Solution
Il existe une façon beaucoup plus simple pour synchroniser nos threads en utilisant la
structure WaitGroup de bibliothèque sync. Je vous dévoile d'abord le code et ensuite je vous
l'explique.
package main
import (
"fmt"
"sync"
"time"
defer [Link]()
[Link](1 * [Link])
func main() {
debut := [Link]()
[Link](1)
go run("Hatim")
[Link](1)
go run("Robert")
[Link](1)
go run("Alex")
[Link]()
fin := [Link]()
[Link]([Link](debut))
Copier
Résultat :
Hatim : 0
Alex : 0
Robert : 0
Alex : 1
Robert : 1
Hatim : 1
Alex : 2
Robert : 2
Hatim : 2
3.0108647s
Ici le mot-clé defer est extrêmement important ! La fonction qui est placée après le mot-
clé defer s'exécutera à chaque fois qu'on quittera notre fonction même en cas de panique
(plantage) de la fonction ! Le mot-clé defer nous garantit alors l'exécution de la
méthode Done(). Si vous supprimez le mot-clé et que votre programme panique (plante)
alors la méthode Done() ne sera jamais exécutée et votre programme tournera en boucle.
Information
Pour information vous pouvez utilisez la méthode Wait() autant de fois que vous voulez.
Lire et écrire dans un fichier
dans le langage de
programmation Go
Ce chapitre va vous apprendre comment vous pouvez
efficacement lire et écrire sur des fichiers à l'aide du langage
de programmation go.
Introduction
Dans ce chapitre, nous allons voir comment vous pouvez efficacement lire et écrire sur
des fichiers à l'aide du langage de programmation go.
Il existe de multiples façons pour lire et écrire dans un fichier. Dans les exemples qui
suivent je vais vous montrer les techniques que j'utilise selon les différents cas
d'utilisation que je juge le plus simple.
package main
import (
"fmt"
"io/ioutil"
func main() {
if err != nil {
[Link](err)
Copier
Résultat :
je suis un fichier de test
package main
import (
"fmt"
"io/ioutil"
"os"
func main() {
if err != nil {
panic(err)
}
_, err = [Link]("test\n") // écrire dans le fichier
if err != nil {
panic(err)
if err != nil {
panic(err)
if err != nil {
[Link](err)
[Link](string(data))
Copier
Information
Résultat :
je suis un fichier de test
test
i love test
Copier
package main
import (
"fmt"
"io/ioutil"
"os"
if e != nil {
panic(e)
check(err)
return string(data)
func main() {
defer [Link]()
check(err)
write("Test\n", file)
data := read([Link]())
[Link](data)
}
Résultat :
je suis un fichier de test
test
i love test
Test
Exercice.
Voilà maintenant vous savez comment lire et écrire dans un fichier. Si vous souhaitez
aller encore plus loin, vous pouvez reprendre le tp du morpions pour rajouter un
système qui vous permet de sauvegarder la partie en cours (tour du joueur et le
damier).
Les channels dans le
langage de programmation
Go
Ce chapitre vous explique les channels en GoLang. Ils
permettent de créer, connecter, synchroniser et communiquer
les différentes goroutines. Vous allez apprendre à créer,
envoyer, recevoir vos channels et à gérer les différents cas
d'erreurs des channels dans le langage de programmation
GO.
Les channels
Déclarer un channel
Pour déclarer votre channel, vous utiliserez le mot-clé make avec le mot-clé chan suivit
du type de donnée que vous souhaitez transiter.
ch := make(chan typeDeValeur)Copier
Pour envoyer ou recevoir une valeur dans un channel, il faut utiliser l'opérateur <-.
Exemple :
package main
import "fmt"
func main() {
go run(canal, "Hatim")
Copier
Résultat :
Hatim
Les envois et les réceptions sur un channel est bloquant par défaut. Qu'est-ce que ça
veut dire ? Lorsqu'une donnée est envoyée à un channel, le contrôle est bloqué dans
l'instruction d'envoi jusqu'à ce qu'une autre goroutine lise depuis ce channel. De la
même manière, lorsque des données sont lues sur un channel, la lecture est bloquée
jusqu'à ce qu'une certaine goroutine écrit des données sur ce channel.
C'est cette propriété des channels qui permet aux goroutines de communiquer
efficacement sans l'utilisation de verrous explicites ou de variables conditionnelles.
package main
import (
"fmt"
"time"
[Link]([Link] * 2)
ch <- name
}
func main() {
now := [Link]()
ch := make(chan string)
[Link]([Link]().Sub(now))
Copier
Résultat :
fonction run() : channel 1
4.00097503s
Nous avons lancé 2 fois la goroutine de la fonction run() avec une lecture sur le
channel ch, nous pouvons remarquer que le temps d’exécution est de 4 secondes
avec un intervalle de 2 secondes entre chaque goroutine, ce qui prouve que les
channels sont bien bloquants.
Par contre ici on ne profite pas forcément de la puissance des goroutines car elles ne
sont pas lancées en simultanée vu que le channel est placé entre chaque goroutine.
Pour régler ce problème je vais changer l'ordre de lecture de nos channels.
package main
import (
"fmt"
"time"
[Link]([Link] * 2)
ch <- name
}
func main() {
now := [Link]()
ch := make(chan string)
[Link]([Link]().Sub(now))
Copier
Résultat :
fonction run() : channel 1
2.000341006s
Information
La première goroutine qui aura écrit sur un channel sera la première à être lu dans
notre programme.
deadlock
Par défaut, les channels sont dit unbuffered, ce qui signifie qu'ils n'accepteront pas
de récepteur ( chan<-) que s'il existe un expéditeur ( <- chan) correspondant prêt à
recevoir la valeur envoyée, l'inverse est aussi vrai.
package main
import (
"fmt"
"sync"
"time"
var wg = [Link]{}
func main() {
now := [Link]()
ch := make(chan int)
// 5 expéditeurs
[Link](1)
go func() {
[Link]([Link] * 2)
i := <-ch
[Link](i)
[Link]()
}()
// 5 récepteurs
go func() {
[Link]([Link] * 2)
ch <- 50
[Link]()
}()
[Link]()
[Link]([Link]().Sub(now))
Copier
Résultat :
50
50
50
50
50
2.000827906s
Ici nous avons autant de récepteurs que d'expéditeurs, donc aucune erreur dans notre
programme, mais maintenant je vais rajouter un récepteur en plus.
package main
import (
"fmt"
"sync"
"time"
var wg = [Link]{}
func main() {
now := [Link]()
ch := make(chan int)
// 5 expéditeur
go func() {
[Link]([Link] * 2)
i := <-ch
[Link](i)
[Link]()
}()
// 6 récepteurs
[Link](1)
go func() {
[Link]([Link] * 2)
ch <- 50
[Link]()
}()
}
[Link]()
[Link]([Link]().Sub(now))
Copier
Erreur :
50
50
50
50
50
D'après le résultat nous pouvons remarquer que les 5 premières goroutines se sont
bien exécutées mais ce n'est pas le cas pour la dernière goroutine. Car cette dernière
n'a aucun expéditeur d'où l'erreur deadlock.
buffered channels
Pour éviter le problème d'avant il est possible de rendre nos channels buffered, c'est-
à-dire de posséder autant de récepteurs que la taille du buffer de notre channel, par
exemple un channel avec une taille de buffer de 30 peut posséder 30 récepteurs.
Pour rendre notre channel buffered, il suffit d'indiquer la longueur de notre buffer
comme second argument de notre canal.
ch := make(chan type-de-valeur, taille_du_buffer)
package main
import (
"fmt"
"sync"
"time"
var wg = [Link]{}
func main() {
now := [Link]()
// 5 expéditeurs
go func() {
[Link]([Link] * 2)
i := <-ch
[Link](i)
[Link]()
}()
// 10 récepteurs
[Link](1)
go func() {
[Link]([Link] * 2)
ch <- 50
[Link]()
}()
}
[Link]()
[Link]([Link]().Sub(now))
Copier
Résultat :
50
50
50
50
50
2.00064941s
Il est possible d'itérer sur un channel buffered en utilisant le mot clé range. Je vais
volontairement provoquer une erreur en itérant sur une channel buffered qui possède
moins de récepteurs que la taille du buffer.
package main
import (
"fmt"
"time"
func main() {
go func() {
}()
[Link](elem)
Copier
Erreur :
test
Pour nous prémunir de cette erreur, il faut indiquer à notre compilateur qu'il n'est pas
nécessaire de lire la suite du buffer de notre channel, cela est possible avec la
fonction close().
package main
import (
"fmt"
"time"
func main() {
go func() {
ch <- "test"
}()
[Link](elem)
Copier
Résultat :
test
Il existe aussi un autre moyen d'itérer sur un channel, mais avant de vous montrez le
code il faut savoir qu'un channel renvoie à la fois sa valeur mais aussi un
booléen qui vous indique si la valeur a été envoyée sur le channel.
package main
import (
"fmt"
func main() {
ch <- "test"
}()
for true {
[Link](elem, ok)
} else {
break
Copier
Résultat :
test
TP créer une application de
chat dans le langage de
programmation Go
Vous avez pu voir beaucoup de notions en GoLang, il est
temps alors de réaliser un Tp en créant une application de
chat dans le langage de programmation GO et d'utiliser les
éléments que vous avez pu voir dans les chapitres
précédents.
Présentation du TP
Nous avons vu beaucoup de notions et je conçois qu'il soit difficile dans certains cas
de comprendre l'intérêt de certaines notions à travers des exemples basiques, et donc
c'est pour ces raisons que j'ai décidé de vous concocter un Tp pour utiliser les
éléments que nous avons pu voir dans les chapitres précédents.
Les exigences
Le serveur
Je vous ai mis des commentaires sur toutes les lignes de code, mais je vous tout de
même vous fournir plus tard quelques explications en plus. Commencez d'abords par
créer un fichier et nommez le [Link].
package main
import (
"fmt"
"net"
)
func gestionErreur(err error) {
if err != nil {
panic(err)
const (
IP = "[Link]" // IP local
func main() {
gestionErreur(err)
// On accepte les connexions entrantes sur le port 3569
if err != nil {
panic(err)
gestionErreur(err)
for {
[Link]("Client:", message)
[Link]([]byte(message + "\n"))
Copier
on a commencé par importer la bibliothèque net, c'est cette bibliothèque qui va nous
aider, à créer notre client et serveur
Un bon serveur se doit d'être disponible 24h/24h 7j/7j "Pas de serveur, pas de clients
🍫" c'est pour cela qu'on utilise la boucle for pour être toujours à l'écoute des
connexions entrantes.
la taille du buffer est de de 4096, donc le message qu'on recevra du client ne peut
dépasser les 4096 bits, le surplus ne sera pas reçu par le client (il est possible d'éviter
ce problème avec la fonction NewReader() de la bibliothèque bufio, je vais utiliser
cette méthode sur le client)
>message := string(buffer[:length])Copier
Si notre message est de taille 16 et notre buffer de taille 4096, il serait plus approprié
d'adapter le buffer selon la taille du message en supprimant le surplus. Cette étape est
très importante si vous souhaitez faire des comparaisons de votre message avec une
autre string (je pense notamment à la vérification du pseudo par le serveur).
Enfin on ne peut envoyer et ne recevoir que des bits (des 0 ou 1) via le protocole TCP
(tous les protocoles d'ailleurs), il est donc important de convertir les messages avant
de les envoyer de string en byte et inversement quand on souhaite les afficher sur
notre écran.
Le client
Côté client ça reste un peu près le même code avec quelques petites modifications.
Créez un fichier et nommez le [Link] avec le code suivant :
package main
import (
"bufio"
"fmt"
"net"
"os"
if err != nil {
panic(err)
const (
IP = "[Link]" // IP local
func main() {
// Connexion au serveur
gestionErreur(err)
for {
// entrée utilisateur
reader := [Link]([Link])
[Link]("client: ")
gestionErreur(err)
[Link]([]byte(text))
gestionErreur(err)
Copier
Ici on n'utilise pas la fonction Listen() mais la fonction Dial() qui nous permet créer
un client et de le connecter à notre serveur, le reste du code ressemble beaucoup au
code du serveur.
Résultat :
Côté serveur :
Client:salut
Client:ça va ?
Côté client :
client: salut
serveur : salut
client: ça va ?
serveur : ça va ?
Vous pensez que c'est fini ? Je vous assure que non car si vous souhaitez rajouter un
deuxième client voila ce qui se passe :
client: test
package main
import (
"bufio"
"fmt"
"net"
)
func gestionErreur(err error) {
if err != nil {
panic(err)
const (
IP = "[Link]"
PORT = "3569"
gestionErreur(err)
[Link]("Client:", string(message))
}
func main() {
gestionErreur(err)
for {
if err == nil {
gestionErreur(err)
buf := [Link](conn)
for {
if err != nil {
[Link]("Client disconnected.\n")
break
}()
Copier
Il faut savoir que par défaut la fonction Accept() est bloquante, elle ne s'exécutera
seulement si le serveur reçoit une nouvelle connexion. Une goroutine est alors créée
propre à chaque nouveau client connecté connexion.
Côté serveur :
Client 1 :
client: slt
serveur : slt
client: toto
serveur : ah
Client 2 :
client: ah
serveur slt
client: tata
serveur ah
Le serveur gère bien plusieurs clients mais par contre côté clients c'est du n'importe au
niveau de l'affichage des messages 🤔.
Le serveur envoie bel et bien les données aux clients, mais les clients ne semblent pas
bonnement synchroniser l'affichage des messages. Pour remédier ce problème il suffit
de créer deux goroutines dans notre client, une goroutine pour gérer la réception des
données et une goroutine pour gérer l'envoi des données.
package main
import (
"bufio"
"fmt"
"net"
"os"
"sync"
if err != nil {
panic(err)
}
const (
IP = "[Link]" // IP local
func main() {
var wg [Link]
// Connexion au serveur
gestionErreur(err)
[Link](2)
for {
reader := [Link]([Link])
gestionErreur(err)
[Link]([]byte(text))
}()
defer [Link]()
for {
gestionErreur(err)
}()
[Link]()
Copier
Résultat :
Client 1 :
slt
serveur : slt
serveur : hoho
serveur : haha
coco
serveur : coco
Client 2 :
hoho
serveur : hoho
haha
serveur : haha
bla bla
serveur : coco
à vous de jouer
Vous ne vous êtes peut être pas rendu compte mais mais on vient de créer une
application de chat ! Par contre ça ne respecte pas encore nos exigences ;). Je vous ai
passé les informations nécessaires pour mener à bien ce TP. Vous n'allez pas être
noté ! Donc prenez le temps d'utiliser au maximum les notions vues sur les chapitres
précédents et bien sûr amusez-vous :).
Solution
Mon petit message
Tout d'abord mon code sera en anglais, la raison est que j'ai l'habitude de coder et
d'écrire mes documentations en anglais, je vous conseille de faire pareil car ça va
vous permettre d'apprendre la langue anglaise avec de nouveaux termes techniques
anglais, cette langue est très importante car la majorité des réponses de vos
recherches sur un moteur de recherche (Google ou autres 😉) seront en anglais.
Deuxièmement, je ne vais pas vous expliquer comme j'ai l'habitude de faire ligne par
ligne mon code. vous avez largement les connaissances nécessaires pour
comprendre mon code mais Je vous ai tout de même mis des commentaires sur
certaines lignes de code et sur toutes les méthodes de mes structures.
Avant de lancer mon code, voici à quoi doit ressembler votre arborescence :
$GOPATH/src/
├── chat-application
│ ├── client
│ │ └── [Link]
│ └── server
│ └── [Link]
└── [Link]
Lancer le serveur :
Lancer le client :
go build [Link]
Si vous êtes sur windows ça vous créera un fichier [Link] et un fichier main sur
linux. Une fois compilé les utilisateurs qui utiliseront votre programme n'auront pas
besoin d'installer le compilateur go vu que sera un fichier binaire.
Conclusion du cours
d'initiation à GoLang
Clap de fin ! Vous connaissez à présent la plupart des
fonctionnalités de base du langage de programmation Go. J'ai
publié cet article pour vous présenter mon message de
conclusion, mes futurs projets/articles sur ce langage et
quelques conseils pour mieux progresser.
Conclusion
Vous voici arrivé(e) au terme de ce cours et je tiens vraiment à vous remercier d'avoir
pris le temps de suivre ce cours et à vous féliciter pour avoir tenu jusqu'au bout 🥇.
En ce qui me concerne, j'ai choisi de vous apprendre le langage Go, car c'est l'un des
langages que j'apprécie le plus, autant vous dire que je me suis vraiment bien régalé à
faire ce cours !
J'espère que la lecture de ce cours vous aura été utile et agréable et que ça vous aura
permis d'y voir un peu plus clair et que ces bases vous auront, je l'espère, donné envie
d'aller encore plus loin.
Je continuerai à faire d'autres tutoriels concernant ce langage. Il peut s'agir d'un article
ou je partage avec vous un simple projet ou script réutilisable. mais
Vous vous rappelez quand je vous ai évoqué au début de ce cours que le langage Go
est utilisé dans plusieurs domaines ? Et bah je ne vous ai pas menti (heureusement
😅), car je viens de découvrir un repository Github vachement sympa qui répertorie
plusieurs frameworks et bibliothèques Go selon le domaine qui vous intéresse.
Lien : [Link]
Sans oublier qu'il existe aussi les bibliothèques officielles de Golang que vous pouvez
retrouver ici.