Tous les programmes Go sont composés de packages.
Les programmes se lancent dans un package nommé main
.
Ce programme utilisent les packages en important les chemins fmt
et math/rand
Par **convention, **le nom d’un package est celui du dernier élément du chemin d’import. Cela signifie que le package comprend les fichier qui commencent par l’instruction package rand
.
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("Mon numéro favori est", rand.Intn(10))
}
On voit que rand est un sous package de math
Et si on regarde ce que fait la fonction intn
alors on voit qu’elle retourne un entier, un entier non négatif entre 0 et n.
Et il panic
(pose une erreur) si n est inférieur ou égal à zéro.
Donc si je lance le code suivant il devrait renvoyer un panic
func main() {
fmt.Println("My favorite number is", rand.Intn(-10))
}
Ce à quoi il répond :
<span notion-color="red">panic: invalid argument to Intn
goroutine 1 [running]:
math/rand.(*Rand).Intn(0x100556c20?, 0xc0000061c0?)
/usr/local/go-faketime/src/math/rand/rand.go:180 +0x4c
math/rand.Intn(0xfffffffffffffff6)
/usr/local/go-faketime/src/math/rand/rand.go:453 +0x25
main.main()
/tmp/sandbox3830465717/prog.go:9 +0x1a</span>
package main
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("Now you have %g problems.\n", math.Sqrt(7))
}
Ce code regroupe deux imports entre parenthèses, c’est une instruction d’import “factorisé”
Vous pouvez également écrire les différents imports unitairement comme ceci :
import "fmt"
import "math"
Mais c’est une bonne pratique d’utiliser l’instruction d’import factorisé.
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.Pi)
}
En Go un nom est exporté si il commence par une lettre capitale, par exemple Pizza
est un nom exporté.
Tout comme la constante Pi
qui existe et est exporté du package math
.
Lorsque l’on importe un package, on peut uniquement utiliser les noms qui sont exportés. Tous les noms qui ne sont pas exportés ne sont pas accessibles en dehors du package. Par exemple si on avait mis l’instruction fmt.Println(math.pi)
Alors on aurait eu l’erreur suivante :
<span notion-color="red">./prog.go:9:19: undefined: math.pi</span>
Go build failed.
package main
import "fmt"
func add(x int, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
Une fonction peu prendre 0 ou n arguments. On déclare une fonction en Go avec le mot clé func
Dans cet exemple on peut voir que la fonction add
prendre deux paramètres de type int
.
Si vous souhaitez en savoir davantage sur les raisons pour lesquelles les types sont déclarés ainsi, consultez l'article sur le blog qui en traite. article on Go's declaration syntax
package main
import "fmt"
func add(x, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
Quand deux ou plusieurs paramètres nommés d’une fonction sont consécutifs et partagent un type, nous pouvons ne pas mettre le type sur jusqu’au dernier paramètre.
Dans cet exemple nous avons raccourci : x int, y int
en x, y int
.
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
Une fonction peut retourner un nombre quelconque de résultats.
Par exemple la fonction swap
renvoie deux chaînes de caractères.
package main
import "fmt"
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
func main() {
fmt.Println(split(17))
}
Les valeurs de retours peuvent être nommées en Go. Elles sont alors traitées en tant que variables définies en haut d’une fonction.
Ces noms doivent être explicites pour donner du sens aux valeurs retournées.
Une instruction return
sans argument va retourner les named return values soit (x, y int)
. On appelle ça un “naked” return.
Les “naked return” doivent être utilisés que dans des fonctions courtes. Car elles ne sont pas simples à lire dans de longues fonctions
package main
import "fmt"
func addAndSayToto(x, y int, z string) (int, string) {
return x + y, z
}
func main() {
addition, name := addAndSayToto(42, 13, "toto")
fmt.Println(addition)
fmt.Println(name)
}
Je voulais vérifier que lors de la déclaration d’une continued function on puisse après deux paramètres consécutifs de même type enchaine sur un autre type.
Enfin je souhaitais récupérer les deux afin d’en différencier les affichages.
L’instruction var
déclare une liste de variables, comme dans les fonctions avec le type en dernier.
L’instruction var peut être au niveau du package ou au niveau d’une fonction. Comme on peut le voir dans cet exemple.
package main
import "fmt"
var c, python, java bool
func main() {
var i int
fmt.Println(i, c, python, java)
}
package main
import "fmt"
var i, j int = 1, 2
func main() {
var c, python, java = true, false, "no!"
fmt.Println(i, j, c, python, java)
}
Une déclaration avec l’instruction var
peut contenir des initializers, un par variable. Si celui-ci est présent alors le type peut être omis.
Go est assez intelligent pour déduire le type à partir de l’initializer.
Dans une fonction l’instruction d’assignation rapide :=
peut être utilisée à la place de l’instruction var avec un type implicite.
Attention ce constructeur n’est pas valable en dehors d’une fonction.
En dehors d’une fonction toutes les instructions commencent avec un mot-clé : var
, func
etc.
Voici les types basiques en Go
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias pour uint8
rune // alias pour int32
// represente un code Unicode
float32 float64
complex64 complex128
L’exemple montre des variables de types différents, et aussi ces déclarations de variables peuvent être factorisés en bloc tout comme les instructions d’import.
Les types int
, uint
, uintptr
sont des types de 32bits sur les systèmes 32bits et 64bits sur des systèmes 64bits.
Quand vous avez besoin d’un entier, utilisez int
tant que vous n’avez pas une raison spécifique d’utiliser les autres.
package main
import (
"fmt"
"math/cmplx"
)
var (
ToBe bool = false
MaxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5 + 12i)
)
func main() {
fmt.Printf("Type: %T Value: %v\n", ToBe, ToBe)
fmt.Printf("Type: %T Value: %v\n", MaxInt, MaxInt)
fmt.Printf("Type: %T Value: %v\n", z, z)
}
Les variables déclarées sans valeurs initiales vont donner leur zero value
La zero value
est :
0
pour les types numeric
false
pour le type boolean
“”
(la chaîne vide) pour les chaînes de caractères
package main
import "fmt"
func main() {
var i int
var f float64
var b bool
var s string
fmt.Printf("%v %v %v %q\n", i, f, b, s)
}
L’expression T(v)
convertit la valeur v
vers le type T
Voici quelques conversions numériques :
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
Ou plus simplement
i := 42
f := float64(i)
u := uint(f)
Contrairement au C, l’assignation entre éléments de types différents nécessite une conversion explicite.
package main
import (
"fmt"
"math"
)
func main() {
var x, y int = 3, 4
var f float64 = math.Sqrt(float64(x*x + y*y))
var z uint = uint(f)
fmt.Println(x, y, z)
}
Allons un peu plus loin , comment Go se comporte lors de conversions particulières ?
package main
import "fmt"
func main() {
i := 123
s := string(i)
fmt.Println("%s", s)
}
Cela m’affiche la valeur x
mais pourquoi ? parce que 123 est la valeur ascii du caractère x
Mais comment afficher “123”
?
Ne venant pas du C et en recherchant sur le web on trouve des réponses
https://stackoverflow.com/questions/11123865/format-a-go-string-without-printing https://stackoverflow.com/questions/10105935/how-to-convert-an-int-value-to-string-in-go
package main
import (
"strconv"
"fmt"
)
func main() {
t := strconv.Itoa(123)
fmt.Println(t)
}
La meilleure solution est celle-ci car l’utilisation de Sprintf
est apparemment plus coûteuse.
Quand on déclare une variable sans spécifier son type , elle est devinée à partir de la valeur de droite.
var i int
j := i // j est donc un int
Mais quand la partie de droite contient une constante numérique sans type, alors sa valeur peut prendre plusieurs types comme int
, float64
ou complex128
selon la précision de la constante.
i := 42 // int
f := 3.142 // float64
g := 0.867 + 0.5i // complex128
package main
import "fmt"
func main() {
v := 42 // change me!
fmt.Printf("v is of type %T\n", v)
}
Une constante peut être déclarée comme des variables, mais avec le mot clé const
Les constantes peuvent être un caractères une chaine de caractères un boolean ou une valeur numérique.
Une constante ne peut être déclarée avec la syntaxe :=
package main
import "fmt"
const Pi = 3.14
func main() {
const World = "世界"
fmt.Println("Hello", World)
fmt.Println("Happy", Pi, "Day")
const Truth = true
fmt.Println("Go rules?", Truth)
}
Les constantes numériques sont des valeurs à haute précision. Une constante non typée prend le type nécessaire selon son contexte.
package main
import "fmt"
const (
// Create a huge number by shifting a 1 bit left 100 places.
// In other words, the binary number that is 1 followed by 100 zeroes.
Big = 1 << 100
// Shift it right again 99 places, so we end up with 1<<1, or 2.
Small = Big >> 99
)
func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
return x * 0.1
}
func main() {
fmt.Println(needInt(Small))
fmt.Println(needFloat(Small))
fmt.Println(needFloat(Big))
}
Félicitations c’est la fin de la première leçon du gotour