Go langage

Mémo go

Uniquement des notes sur ce qui est “original” dans le langage “go”, par rapport aux autres langages.

Variables

Déclaration

Par rapport à tous les autres langages, il faut le faire à l’envers :
var x int
Et pour un tableau : var [nom] [[longueur]][type], exemple :
var s [3]int

Initialisation avec des valeurs

Pas d’explication, un exemple direct suffit : arr := [2]int{2, 4}

Inférence de type

Une déclaration complète d’une variable serait var i int = 12
Une déclaration ultra simplifiée acceptable est i := 12 et dans ce cas, Go va tenter de deviner le type (“type inference”).

Comparaison de tableaux

La déclaration de variables se fait toujours en précisant la longueur, et au moment de comparer, il faut que les tableaux aient exactement la même longueur, sinon on aura une erreur. C’est pour cela que les tableaux sont moins utilisés que le type slice.

Comparaison = avoir des types identiques

Go n’essaie jamais de transformer les variables en booléens pour pouvoir les comparer, c’est à nous transformer une variable ou l’autre de manière à ce qu’elles aient toujours le même type.

Création d’une variable dans un “if”

Exemple direct : if b:= a / 2; b > 5 {...} else {...}
La variable b n’existera que dans le bloc if / else.

Boucle for / range

s := "Bonjour"
for k, v := range s {
    fmt.Println(k, v, string(v))
}

k correspond à l’index, et n’est pas toujours incrémenté que de 1, selon l’encodage. C’est la méthode à utiliser pour parcourir une chaîne en utf8. Car via un classique for, Go parcourra la chaîne “à la” C, c’est à dire que un caractère = un octet (ce qui n’est pas le cas en utf8).

Switch / case

Par défaut, les “break” sont implicites, pas besoin de les mettre. Si on veut exécuter l’instruction du “case” suivant (= comme en C ou JavaScript), il faut le dire explicitement via “fallthrough“.

Il est possible de créer une variable qui ne vivra que le temps du switch / case, exemple :

switch l := len(word); word {
    ...blabla...
}

Contrairement aux autres langages, il ne faut pas mettre plusieurs case d’affilée pour gérer plusieurs cas au même endroit, il suffit de les séparer par une virgule, exemple pour continuer le précédent :

switch l := len(word); word {
case 1 < l && l < 10, word == "ok":
    fmt.Println("Soit mot : 1 < longueur < 10, soit il vaut 'ok'")
}

Fonctions

Appels

Tous les appels de fonction sont par valeur. Jamais par référence. Nombre, booléen, ainsi que les tableaux.

Retours

Le type résultat de retour est après les parenthèses des paramètres et avant l’accolade ouvrante :
func mult(a, b) int {
    return a + b
}

Vous pouvez renvoyer plusieurs valeurs ! Les résultats de retour sont entre parenthèse après les parenthèses des paramètres et avant l’accolade ouvrante :
func divAndRemainder(a, b) (int, int) {
    return a / b, a % b
}
d, r := divAndRemainder(2, 3)
d, _ := divAndRemainder(2, 3)
_, d := divAndRemainder(2, 3)

Vous pouvez nommer les paramètres de retour et les “remplir” dans la fonction. Il faut simplement ajouter return sans rien pour qu’il renvoie le retour sans y toucher :
func divAndRemainder(a, b) (div, remainder int) {
    div = a / b
    remainder = a % b
    return
}
d, r := divAndRemainder(2, 3)
d, _ := divAndRemainder(2, 3)
_, d := divAndRemainder(2, 3)

Fonctions en tant que variable

Exemple direct :
func myFunc() {
    f := func(a int) int {
        return a + 1
    }
    fmt.Println(f(1))
}

Fonctions en tant que paramètres

Exemple direct d’un paramètre qui doit être “une fonction qui prend un entier en tant que paramètre et renvoie un entier” :
func myFunc(a int, f func(int) int) {
    fmt.Println(f(a))
}

Tableaux

Base : var a [2]int

fmt.Println("Array a", a, len(a))

Mieux : slice var a []int

La différence entre slice et array ? On ne précise pas le nombre d’éléments.

Pointeurs

Très proche du C. Un exemple direct suffit :
var p *int
i := 4 // int declaration
p = &i // assign the address of the variable to the pointer
*p = 45 // change the value of the variable i
fmt.Println(p, *p) // prints address then value

Switch / case

C’est l’inverse du C : les break sont implicites, et si on veut vraiment continuer sur le case suivant, il faut utiliser le mot-clé fallthrough :

switch i := myfunc(); {
case i < 0:
    fmt.Println("negative")
    fallthrough
case i == 0:
    fmt.Println("zero")
default:
    fmt.Println("positive")
}

Conversions

Bizzareries

Pointeurs et structures

Vous pouvez déclarer une structure, et la créer dynamiquement, puis créer un pointeur vers elle. En C, quand on utilise les pointeurs, l’indirection pour accéder aux membres est "->", donc on ne peut pas se tromper. Ici, on utilise la même syntaxe, que ce soit avec une structure, directement, ou avec un pointeur, donc indirectement. Exemple concret :

type People struct {
    name string
    height int
}
var value People
value.name = "Olivier"
value.height = 190
ptr := &value
ptr.name = "Thibault"
ptr.height = 185

Donc on écrit value.name et ptr.name alors que l’un est une struct et l’autre un pointeur. Je n’aime pas du tout cela, cela prête clairement à confusion.

Astuces classiques

Site officiel

Packages