FONCTION
Déclaration d’une fonction : utilisez le mot-clé func
suivi du nom de la fonction. Si votre fonction prend des paramètres, vous devez en spécifier le type dans le cas ou elle retourne quelque chose, le type doit être spécifié.
func add(a int, b int) int {
return a + b
}
// or a,b int
func main() {
result := add(3, 5)
fmt.Println("The sum is:", result)
}
Paramètres optionnels
Go ne vous autorise pas a avoir des parametres optionels mais vous pouvez en simulez avec des struct
type Options struct {
options1 bool
options2 bool
options3 bool
}
func Optional( opts Options){
}
Optional(Options{true, true})
variadique parametres
Go prend également en charge ce qu’on appelle des paramètres variadiques, c’est-à-dire un nombre variable d’arguments passés à une fonction.
func VariadicAdd(a int, b ...int) int {
x := 0
for _,v := range b {
x = x + v
}
return x + a
}
VariadicAdd(1, 545, 44, 54)
Remarque : Le paramètre variadique doit toujours être specifier en dernier dans la liste des paramètres
Multiple valeur de retour
Vous avez la possibilité d’avoir plusieurs valeur de retour.
// code du module path de la bibliotheque go
func Split(path string) (dir, file string) {
i := bytealg.LastIndexByteString(path, '/')
return path[:i+1], path[i+1:]
}
comme vous pouvez le voir dans l’exemple vous avez aussi la possibilité de nommé la valeur de retour.
Fonction anonymes
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
func(j int) {
fmt.Println("affiche", j, "depuis l'interieur")
}(i)
}
}
Exactement comme une fonction normal mais sans la nommer
Closure
Les fonctions déclarées à l’intérieur de fonctions sont spéciales, ce sont des fermetures (closures). Il s’agit d’un mot informatique qui signifie que les fonctions déclarées à l’intérieur de fonctions peuvent accéder aux variables déclarées dans la fonction extérieure et les modifier.
exemple d’usage :
package main
import (
"fmt"
"sort"
)
func main() {
a := []int{1, 2, 4}
sort.Slice(a, func(i, j int) bool {
return a[i] > a[j]
})
fmt.Println(a)
}
Defer
Le mot-clé defer
permet d’exécuter une fonction lorsque la fonction principale se termine ou que son exécution est interrompue.
func OpenFileWithPath(path string) ([]byte, error) {
f, err := os.Open(path)
defer f.Close()
if err != nil {
return nil, err
}
content, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
return content, nil
}
Quand il y a plusieurs defer, il s’execute in LIFO (Last in first out) defer peut être utiliser avec des function anonyme
Utilisation des valeurs de retour nommées avec defer
Il est possible qu’une fonction différée (defer
) examine ou modifie les valeurs de retour de la fonction qui l’entoure, notamment en utilisant des valeurs de retour nommées. Cela permet de gérer les erreurs de manière plus efficace, notamment dans les transactions de base de données.
Exemple avec une transaction de base de données :
// source learn go in an idiomatic way
func DoSomeInserts(ctx context.Context, db *sql.DB, value1, value2 string) (err error) {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() {
if err == nil {
err = tx.Commit()
}
if err != nil {
tx.Rollback()
}
}()
_, err = tx.ExecContext(ctx, "INSERT INTO FOO (val) VALUES ($1)", value1)
if err != nil {
return err
}
return nil
}
Modèle commun pour la gestion des ressources avec des closures
En Go, une fonction qui alloue une ressource peut également retourner une closure pour nettoyer cette ressource après utilisation. Un exemple simple est l’ouverture d’un fichier et la gestion de sa fermeture.
Exemple de fonction de gestion de fichier :
func getFile(name string) (*os.File, func(), error) {
file, err := os.Open(name)
if err != nil {
return nil, nil, err
}
return file, func() {
file.Close()
}, err
}
f, closer, err := getFile(os.Args[1])
if err != nil {
log.Fatal(err)
}
defer closer()
Go : Passage par valeur et comportements des types
Go est un langage call by value (passage par valeur), ce qui signifie que lorsqu’une variable est passée à une fonction, Go crée une copie de la valeur de la variable.
Exemple avec des types de base et structs :
type person struct {
age int
name string
}
func modifyFails(i int, s string, p person) {
i = i * 2
s = "Goodbye"
p.name = "Bob"
}
func main() {
p := person{}
i := 2
s := "Hello"
modifyFails(i, s, p)
fmt.Println(i, s, p) // Résultat : 2, "Hello", {0 ""}
}
Dans cet exemple, les modifications des paramètres au sein de la fonction ne persistent pas, car Go fait une copie des valeurs des variables. Cela inclut également les structs.
Comportement des maps et slices :
Les maps et slices se comportent différemment car ils sont implémentés avec des pointeurs.
func modMap(m map[int]string) {
m[2] = "hello"
m[3] = "goodbye"
delete(m, 1)
}
func modSlice(s []int) {
for k, v := range s {
s[k] = v * 2
}
s = append(s, 10) // Ne modifie pas la longueur du slice d'origine
}
func main() {
m := map[int]string{1: "first", 2: "second"}
modMap(m)
fmt.Println(m) // Résultat : map[2:hello 3:goodbye]
s := []int{1, 2, 3}
modSlice(s)
fmt.Println(s) // Résultat : [2 4 6]
}
Les modifications sur les maps sont directement appliquées, mais pour les slices, bien que l’on puisse modifier leurs éléments, l’ajout d’éléments via append ne modifie pas le slice original.