Tutoriel Golang : Apprenez le langage de programmation Go pour les débutants
C'est quoi Go ?
Go (également connu sous le nom de Golang) est un langage de programmation open source développé par Google. C'est un langage compilé à typage statique. Go prend en charge la programmation simultanée, c'est-à-dire qu'il permet d'exécuter plusieurs processus simultanément. Ceci est réalisé à l'aide de canaux, de goroutines, etc. Go Language dispose d'un garbage collection qui gère lui-même la mémoire et permet l'exécution différée des fonctions.
Nous apprendrons toutes les bases de Golang dans ce didacticiel Learn Go Language.
Comment télécharger et installer GO
Étape 1) Cliquez sur https://golang.org/dl/. Téléchargez le binaire pour votre système d'exploitation.
Étape 2) Double cliquez sur le programme d'installation et cliquez sur Exécuter.
Étape 3) Cliquez Sur Suivant
Étape 4) Sélectionnez le dossier d'installation et cliquez sur Suivant.
Étape 5) Cliquez sur Terminer une fois l'installation terminée.
Étape 6) Une fois l'installation terminée, vous pouvez la vérifier en ouvrant le terminal et en tapant
go version
Cela affichera la version de go installée
Votre programme First Go – Go Hello World !
Créez un dossier appelé StudyGo. Dans ce didacticiel du langage Go, nous créerons nos programmes Go dans ce dossier. Les fichiers Go sont créés avec l'extension .aller. Vous pouvez exécuter des programmes Go en utilisant la syntaxe
go run <filename>
Créez un fichier appelé first.go, ajoutez-y le code ci-dessous et enregistrez
package main import ("fmt") func main() { fmt.Println("Hello World! This is my first Go program\n") }
Accédez à ce dossier dans votre terminal. Exécutez le programme à l'aide de la commande
va courir en premier, va
Vous pouvez voir l'impression de sortie
Hello World! This is my first Go program
Parlons maintenant du programme ci-dessus.
package main – Chaque programme Go Language doit commencer par un nom de package. Go nous permet d'utiliser des packages dans d'autres programmes Go et prend donc en charge la réutilisation du code. L'exécution d'un programme Go commence par le code contenu dans le package nommé main.
import fmt – importe le package fmt. Ce package implémente les fonctions d'E/S.
func main() – C'est la fonction à partir de laquelle commence l'exécution du programme. La fonction main doit toujours être placée dans le package principal. Sous main(), vous pouvez écrire le code à l’intérieur de { }.
fmt.Println – Cela imprimera le texte à l’écran grâce à la fonction Println de fmt.
Remarque : dans les sections ci-dessous de ce didacticiel Go, lorsque vous mentionnez exécuter/exécuter le code, cela signifie enregistrer le code dans un fichier avec l'extension .go et l'exécuter en utilisant la syntaxe.
go run <filename>
Types de données
Les types (types de données) représentent le type de la valeur stockée dans une variable, le type de la valeur renvoyée par une fonction, etc.
Il existe trois types de base dans Go Language
Types numériques – Représente des valeurs numériques qui incluent des valeurs entières, à virgule flottante et complexes. Différents types numériques sont :
int8 – entiers signés 8 bits.
int16 – entiers signés 16 bits.
int32 – entiers signés 32 bits.
int64 – entiers signés 64 bits.
uint8 – entiers non signés de 8 bits.
uint16 – entiers non signés de 16 bits.
uint32 – entiers non signés de 32 bits.
uint64 – entiers non signés de 64 bits.
float32 – Nombres à virgule flottante 32 bits.
float64 – Nombres à virgule flottante 64 bits.
complex64 – a float32 parties réelles et imaginaires.
complex128 – a float32 parties réelles et imaginaires.
Types de chaînes – Représente une séquence d’octets (caractères). Vous pouvez effectuer diverses opérations sur des chaînes comme la concaténation de chaînes, l'extraction de sous-chaînes, etc.
Types booléens – Représente 2 valeurs, vraies ou fausses.
Interface Golang
Interface Golang est une collection de signatures de méthodes utilisées par un type pour implémenter le comportement des objets. L'objectif principal de l'interface Golang est de fournir des signatures de méthodes avec des noms, des arguments et des types de retour. C'est à un Type de déclarer et d'implémenter la méthode. Une interface dans Golang peut être déclarée à l'aide du mot-clé « interface ».
Variables
Les variables pointent vers un emplacement mémoire qui stocke une sorte de valeur. Le paramètre type (dans la syntaxe ci-dessous) représente le type de valeur qui peut être stockée dans l'emplacement mémoire.
La variable peut être déclarée en utilisant la syntaxe
var <variable_name> <type>
Une fois que vous avez déclaré une variable d'un type, vous pouvez affecter la variable à n'importe quelle valeur de ce type.
Vous pouvez également donner une valeur initiale à une variable lors de la déclaration elle-même en utilisant
var <variable_name> <type> = <value>
Si vous déclarez la variable avec une valeur initiale, allez et déduire le type de la variable à partir du type de valeur attribuée. Vous pouvez donc omettre le type lors de la déclaration en utilisant la syntaxe
var <variable_name> = <value>
De plus, vous pouvez déclarer plusieurs variables avec la syntaxe
var <variable_name1>, <variable_name2> = <value1>, <value2>
Le programme ci-dessous dans ce didacticiel Go contient quelques exemples Golang de déclarations de variables
package main import "fmt" func main() { //declaring a integer variable x var x int x=3 //assigning x the value 3 fmt.Println("x:", x) //prints 3 //declaring a integer variable y with value 20 in a single statement and prints it var y int=20 fmt.Println("y:", y) //declaring a variable z with value 50 and prints it //Here type int is not explicitly mentioned var z=50 fmt.Println("z:", z) //Multiple variables are assigned in single line- i with an integer and j with a string var i, j = 100,"hello" fmt.Println("i and j:", i,j) }
La sortie sera
x: 3 y: 20 z: 50 i and j: 100 hello
Go Language fournit également un moyen simple de déclarer les variables avec une valeur en omettant le mot-clé var en utilisant
<variable_name> := <value>
Notez que vous avez utilisé := au lieu de =. Vous ne pouvez pas utiliser := simplement pour attribuer une valeur à une variable déjà déclarée. := est utilisé pour déclarer et attribuer une valeur.
Créez un fichier appelé assign.go avec le code suivant
package main import ("fmt") func main() { a := 20 fmt.Println(a) //gives error since a is already declared a := 30 fmt.Println(a) }
Exécutez go run assign.go pour voir le résultat comme
./assign.go:7:4: no new variables on left side of :=
Les variables déclarées sans valeur initiale auront 0 pour les types numériques, false pour les booléens et une chaîne vide pour les chaînes
Constants
Les variables constantes sont les variables dont la valeur ne peut pas être modifiée une fois attribuée. Une constante dans le langage de programmation Go est déclarée en utilisant le mot-clé « const »
Créez un fichier appelé constant.go et avec le code suivant
package main import ("fmt") func main() { const b =10 fmt.Println(b) b = 30 fmt.Println(b) }
Exécutez go run constant.go pour voir le résultat comme
.constant.go:7:4: cannot assign to b
Exemples de boucles For
Les boucles sont utilisées pour exécuter un bloc d'instructions à plusieurs reprises en fonction d'une condition. La plupart des langages de programmation proposent 3 types de boucles : for, while, do while. Mais le langage de programmation Go ne prend en charge que la boucle.
La syntaxe d'une boucle Golang for est
for initialisation_expression; evaluation_expression; iteration_expression{ // one or more statement }
L'expression_initialisation est exécutée en premier (et une seule fois) dans la boucle Golang for.
Ensuite, l'expression_évaluation est évaluée et si elle est vraie, le code à l'intérieur du bloc est exécuté.
L’identifiant iteration_expression est exécuté et l’évaluation_expression est à nouveau évaluée. Si c'est vrai, le bloc d'instructions est à nouveau exécuté. Cela continuera jusqu'à ce que évaluation_expression devienne fausse.
Copiez le programme ci-dessous dans un fichier et exécutez-le pour voir le Golang pour les numéros d'impression en boucle de 1 à 5.
package main import "fmt" func main() { var i int for i = 1; i <= 5; i++ { fmt.Println(i) } }
La sortie est
1 2 3 4 5
Sinon
If else est une instruction conditionnelle. La synaxe est
if condition{ // statements_1 }else{ // statements_2 }
Ici, la condition est évaluée et si elle est vraie, les instructions_1 seront exécutées, sinon les instructions_2 seront exécutées.
Vous pouvez également utiliser l'instruction if sans autre. Vous pouvez également avoir enchaîné des instructions if else. Les programmes ci-dessous vous expliqueront davantage si autrement.
Exécutez le programme ci-dessous. Il vérifie si un nombre, x, est inférieur à 10. Si tel est le cas, il affichera « x est inférieur à 10 ».
package main import "fmt" func main() { var x = 50 if x < 10 { //Executes if x < 10 fmt.Println("x is less than 10") } }
Ici, puisque la valeur de x est supérieure à 10, l’instruction à l’intérieur de la condition de bloc if ne sera pas exécutée.
Voyez maintenant le programme ci-dessous. Dans ce didacticiel du langage de programmation Go, nous avons un bloc else qui sera exécuté en cas d'échec de l'évaluation if.
package main import "fmt" func main() { var x = 50 if x < 10 { //Executes if x is less than 10 fmt.Println("x is less than 10") } else { //Executes if x >= 10 fmt.Println("x is greater than or equals 10") } }
Ce programme vous donnera une sortie
x is greater than or equals 10
Maintenant, dans ce didacticiel Go, nous verrons un programme avec plusieurs blocs if else (enchaînés if else). Exécutez l’exemple Go ci-dessous. Il vérifie si un nombre est inférieur à 10 ou compris entre 10 et 90 ou supérieur à 90.
package main import "fmt" func main() { var x = 100 if x < 10 { //Executes if x is less than 10 fmt.Println("x is less than 10") } else if x >= 10 && x <= 90 { //Executes if x >= 10 and x<=90 fmt.Println("x is between 10 and 90") } else { //Executes if both above cases fail i.e x>90 fmt.Println("x is greater than 90") } }
Ici, la condition if vérifie d'abord si x est inférieur à 10 et ce n'est pas le cas. Il vérifie donc la condition suivante (sinon si) si elle est comprise entre 10 et 90, ce qui est également faux. Il exécute donc le bloc sous la section else qui donne le résultat
x is greater than 90
Basculer
Switch est une autre instruction conditionnelle. Les instructions Switch évaluent une expression et le résultat est comparé à un ensemble de valeurs disponibles (cas). Une fois qu'une correspondance est trouvée, les instructions associées à cette correspondance (cas) sont exécutées. Si aucune correspondance n'est trouvée, rien ne sera exécuté. Vous pouvez également ajouter un cas par défaut à switch qui sera exécuté si aucune autre correspondance n'est trouvée. La syntaxe du commutateur est
switch expression { case value_1: statements_1 case value_2: statements_2 case value_n: statements_n default: statements_default }
Ici, la valeur de l'expression est comparée aux valeurs de chaque cas. Une fois qu'une correspondance est trouvée, les instructions associées à ce cas sont exécutées. Si aucune correspondance n'est trouvée, les instructions de la section par défaut sont exécutées.
Exécutez le programme ci-dessous
package main import "fmt" func main() { a,b := 2,1 switch a+b { case 1: fmt.Println("Sum is 1") case 2: fmt.Println("Sum is 2") case 3: fmt.Println("Sum is 3") default: fmt.Println("Printing default") } }
Vous obtiendrez la sortie comme
Sum is 3
Changez la valeur de a et b à 3 et le résultat sera
Printing default
Vous pouvez également avoir plusieurs valeurs dans une casse en les séparant par une virgule.
Arrays
Le tableau représente une taille fixe, nommée séquence d'éléments du même type. Vous ne pouvez pas avoir un tableau contenant à la fois des entiers et des caractères. Vous ne pouvez pas modifier la taille d'un tableau une fois que vous avez défini la taille.
La syntaxe pour déclarer un tableau est
var arrayname [size] type
Chaque élément du tableau peut se voir attribuer une valeur en utilisant la syntaxe
arrayname [index] = value
L'index du tableau commence à partir de 0 à taille-1.
Vous pouvez attribuer des valeurs aux éléments du tableau lors de la déclaration en utilisant la syntaxe
arrayname := [size] type {value_0,value_1,…,value_size-1}
Vous pouvez également ignorer le paramètre size lors de la déclaration du tableau avec des valeurs en remplaçant size par ... et le compilateur trouvera la longueur à partir du nombre de valeurs. La syntaxe est
arrayname := […] type {value_0,value_1,…,value_size-1}
Vous pouvez trouver la longueur du tableau en utilisant la syntaxe
len(arrayname)
Exécutez l'exemple Go ci-dessous pour comprendre le tableau
package main import "fmt" func main() { var numbers [3] string //Declaring a string array of size 3 and adding elements numbers[0] = "One" numbers[1] = "Two" numbers[2] = "Three" fmt.Println(numbers[1]) //prints Two fmt.Println(len(numbers)) //prints 3 fmt.Println(numbers) // prints [One Two Three] directions := [...] int {1,2,3,4,5} // creating an integer array and the size of the array is defined by the number of elements fmt.Println(directions) //prints [1 2 3 4 5] fmt.Println(len(directions)) //prints 5 //Executing the below commented statement prints invalid array index 5 (out of bounds for 5-element array) //fmt.Println(directions[5]) }
Sortie
Two 3 [One Two Three] [1 2 3 4 5] 5
Golang Slice et fonction d'ajout
Une tranche est une partie ou un segment d'un tableau. Ou bien il s'agit d'une vue ou d'une vue partielle d'un tableau sous-jacent vers lequel il pointe. Vous pouvez accéder aux éléments d'une tranche en utilisant le nom de la tranche et le numéro d'index comme vous le faites dans un tableau. Vous ne pouvez pas modifier la longueur d'un tableau, mais vous pouvez modifier la taille d'une tranche.
Le contenu d'une tranche est en fait les pointeurs vers les éléments d'un tableau. Ça veut dire si vous modifiez un élément dans une tranche, le contenu du tableau sous-jacent sera également affecté.
La syntaxe pour créer une tranche est
var slice_name [] type = array_name[start:end]
Cela créera une tranche nommée slice_name à partir d'un tableau nommé array_name avec les éléments de l'index du début à la fin-1.
Maintenant, dans ce didacticiel Golang, nous allons exécuter le programme ci-dessous. Le programme créera une tranche à partir du tableau et l'imprimera. De plus, vous pouvez voir que la modification du contenu de la tranche modifiera le tableau réel.
package main import "fmt" func main() { // declaring array a := [5] string {"one", "two", "three", "four", "five"} fmt.Println("Array after creation:",a) var b [] string = a[1:4] //created a slice named b fmt.Println("Slice after creation:",b) b[0]="changed" // changed the slice data fmt.Println("Slice after modifying:",b) fmt.Println("Array after slice modification:",a) }
Cela imprimera le résultat comme
Array after creation: [one two three four five] Slice after creation: [two three four] Slice after modifying: [changed three four] Array after slice modification: [one changed three four five]
Il existe certaines fonctions comme Golang len, Golang append que vous pouvez appliquer sur les tranches
len(nom_tranche) – renvoie la longueur de la tranche
append(nom_slice, valeur_1, valeur_2) – Golang append est utilisé pour ajouter value_1 et value_2 à une tranche existante.
append(slice_nale1,slice_name2…) – ajoute slice_name2 à slice_name1
Exécutez le programme suivant.
package main import "fmt" func main() { a := [5] string {"1","2","3","4","5"} slice_a := a[1:3] b := [5] string {"one","two","three","four","five"} slice_b := b[1:3] fmt.Println("Slice_a:", slice_a) fmt.Println("Slice_b:", slice_b) fmt.Println("Length of slice_a:", len(slice_a)) fmt.Println("Length of slice_b:", len(slice_b)) slice_a = append(slice_a,slice_b...) // appending slice fmt.Println("New Slice_a after appending slice_b :", slice_a) slice_a = append(slice_a,"text1") // appending value fmt.Println("New Slice_a after appending text1 :", slice_a) }
La sortie sera
Slice_a: [2 3] Slice_b: [two three] Length of slice_a: 2 Length of slice_b: 2 New Slice_a after appending slice_b : [2 3 two three] New Slice_a after appending text1 : [2 3 two three text1]
Le programme crée d'abord 2 tranches et imprime sa longueur. Ensuite, il a ajouté une tranche à une autre, puis une chaîne à la tranche résultante.
Les fonctions
Une fonction représente un bloc d'instructions qui effectue une tâche spécifique. Une déclaration de fonction nous indique le nom de la fonction, le type de retour et les paramètres d'entrée. La définition de fonction représente le code contenu dans la fonction. La syntaxe pour déclarer la fonction est
func function_name(parameter_1 type, parameter_n type) return_type { //statements }
Les paramètres et les types de retour sont facultatifs. En outre, vous pouvez renvoyer plusieurs valeurs à partir d'une fonction.
Maintenant, dans ce didacticiel Golang, exécutons l'exemple Golang suivant. Ici, la fonction nommée calc acceptera 2 nombres, effectuera l'addition et la soustraction et retournera les deux valeurs.
package main import "fmt" //calc is the function name which accepts two integers num1 and num2 //(int, int) says that the function returns two values, both of integer type. func calc(num1 int, num2 int)(int, int) { sum := num1 + num2 diff := num1 - num2 return sum, diff } func main() { x,y := 15,10 //calls the function calc with x and y an d gets sum, diff as output sum, diff := calc(x,y) fmt.Println("Sum",sum) fmt.Println("Diff",diff) }
La sortie sera
Sum 25 Diff 5
nos différents forfaits
Les packages sont utilisés pour organiser le code. Dans un gros projet, il n’est pas possible d’écrire du code dans un seul fichier. Le langage de programmation Go nous permet d'organiser le code sous différents packages. Cela augmente la lisibilité et la réutilisabilité du code. Un programme Go exécutable doit contenir un package nommé main et l'exécution du programme démarre à partir de la fonction nommée main. Vous pouvez importer d'autres packages dans notre programme en utilisant la syntaxe
import package_name
Nous verrons et discuterons dans ce didacticiel Golang de la façon de créer et d'utiliser des packages dans l'exemple Golang suivant.
Étape 1) Créez un fichier appelé package_example.go et ajoutez le code ci-dessous
package main import "fmt" //the package to be created import "calculation" func main() { x,y := 15,10 //the package will have function Do_add() sum := calculation.Do_add(x,y) fmt.Println("Sum",sum) }
Dans le programme ci-dessus, fmt est un package que le langage de programmation Go nous fournit principalement à des fins d'E/S. Vous pouvez également voir un package nommé calcul. À l'intérieur de main(), vous pouvez voir une somme d'étape := calcul.Do_add(x,y). Cela signifie que vous appelez la fonction Do_add à partir du calcul du package.
Étape 2) Tout d’abord, vous devez créer le calcul du package dans un dossier du même nom sous le dossier src de go. Le chemin installé de go peut être trouvé à partir de la variable PATH.
Pour Mac, trouvez le chemin en exécutant echo $PATH
Le chemin est donc /usr/local/go
Pour Windows, recherchez le chemin en exécutant echo %GOROOT%
Ici, le chemin est C:\Go\
Étape 3) Accédez au dossier src (/usr/local/go/src pour Mac et C:\Go\src pour Windows). Maintenant, à partir du code, le nom du package est calcul. Go nécessite que le package soit placé dans un répertoire du même nom sous le répertoire src. Créez un répertoire nommé calcul dans le dossier src.
Étape 4) Créez un fichier appelé calc.go (vous pouvez donner n'importe quel nom, mais le nom du package dans le code est important. Ici, il devrait s'agir de calcul) dans le répertoire de calcul et ajoutez le code ci-dessous.
package calculation func Do_add(num1 int, num2 int)(int) { sum := num1 + num2 return sum }
Étape 5) Exécutez la commande go install depuis le répertoire de calcul qui compilera le calc.go.
Étape 6) Revenez maintenant à package_example.go et exécutez go run package_example.go. La sortie sera Somme 25.
Notez que le nom de la fonction Do_add commence par une lettre majuscule. En effet, dans Go, si le nom de la fonction commence par une lettre majuscule, cela signifie que d'autres programmes peuvent le voir (y accéder), sinon d'autres programmes ne peuvent pas y accéder. Si le nom de la fonction était do_add , alors vous auriez eu l'erreur
ne peut pas faire référence au nom non exporté calculator.calc..
Report et cumul des reports
Les instructions defer sont utilisées pour différer l'exécution d'un appel de fonction jusqu'à ce que la fonction qui contient l'instruction defer termine son exécution.
Apprenons cela avec un exemple :
package main import "fmt" func sample() { fmt.Println("Inside the sample()") } func main() { //sample() will be invoked only after executing the statements of main() defer sample() fmt.Println("Inside the main()") }
La sortie sera
Inside the main() Inside the sample()
Ici, l'exécution de sample() est différée jusqu'à ce que l'exécution de la fonction englobante (main()) soit terminée.
Le report d’empilement utilise plusieurs instructions de report. Supposons que vous ayez plusieurs instructions defer dans une fonction. Go place tous les appels de fonction différés dans une pile, et une fois la fonction englobante renvoyée, les fonctions empilées sont exécutées dans le Ordre Dernier entré, premier sorti (LIFO). Vous pouvez le voir dans l’exemple ci-dessous.
Exécutez le code ci-dessous
package main import "fmt" func display(a int) { fmt.Println(a) } func main() { defer display(1) defer display(2) defer display(3) fmt.Println(4) }
La sortie sera
4 3 2 1
Ici, le code à l'intérieur de main() s'exécute en premier, puis les appels de fonction différés sont exécutés dans l'ordre inverse, c'est-à-dire 4, 3,2,1.
Pointers
Avant d'expliquer les pointeurs, parlons d'abord de l'opérateur '&'. L'opérateur '&' est utilisé pour obtenir l'adresse d'une variable. Cela signifie que '&a' imprimera l'adresse mémoire de la variable a.
Dans ce tutoriel Golang, nous exécuterons le programme ci-dessous pour afficher la valeur d'une variable et l'adresse de cette variable
package main import "fmt" func main() { a := 20 fmt.Println("Address:",&a) fmt.Println("Value:",a) }
Le résultat sera
Address: 0xc000078008 Value: 20
Une variable pointeur stocke l’adresse mémoire d’une autre variable. Vous pouvez définir un pointeur en utilisant la syntaxe
var variable_name *type
L'astérisque (*) représente la variable qui est un pointeur. Vous comprendrez mieux en exécutant le programme ci-dessous
package main import "fmt" func main() { //Create an integer variable a with value 20 a := 20 //Create a pointer variable b and assigned the address of a var b *int = &a //print address of a(&a) and value of a fmt.Println("Address of a:",&a) fmt.Println("Value of a:",a) //print b which contains the memory address of a i.e. &a fmt.Println("Address of pointer b:",b) //*b prints the value in memory address which b contains i.e. the value of a fmt.Println("Value of pointer b",*b) //increment the value of variable a using the variable b *b = *b+1 //prints the new value using a and *b fmt.Println("Value of pointer b",*b) fmt.Println("Value of a:",a)}
La sortie sera
Address of a: 0x416020 Value of a: 20 Address of pointer b: 0x416020 Value of pointer b 20 Value of pointer b 21 Value of a: 21
Structure
Une structure est un type de données défini par l'utilisateur qui contient lui-même un élément supplémentaire du même type ou d'un type différent.
L'utilisation d'une structure est un processus en 2 étapes.
Tout d’abord, créez (déclarez) un type de structure
Deuxièmement, créez des variables de ce type pour stocker des valeurs.
Les structures sont principalement utilisées lorsque vous souhaitez stocker des données associées ensemble.
Considérez une information sur l'employé qui contient son nom, son âge et son adresse. Vous pouvez gérer cela de 2 manières
Créez 3 tableaux : un tableau stocke les noms des employés, un autre stocke l'âge et le troisième stocke l'âge.
Déclarez un type de structure avec 3 champs : nom, adresse et âge. Créez un tableau de ce type de structure où chaque élément est un objet de structure ayant un nom, une adresse et un âge.
La première approche n'est pas efficace. Dans ce genre de scénarios, les structures sont plus pratiques.
La syntaxe pour déclarer une structure est
type structname struct { variable_1 variable_1_type variable_2 variable_2_type variable_n variable_n_type }
Un exemple de déclaration de structure est
type emp struct { name string address string age int }
Ici, un nouveau type défini par l'utilisateur nommé emp est créé. Maintenant, vous pouvez créer des variables du type emp en utilisant la syntaxe
var variable_name struct_name
Un exemple est
var empdata1 emp
Vous pouvez définir des valeurs pour empdata1 comme
empdata1.name = "John" empdata1.address = "Street-1, Bangalore" empdata1.age = 30
Vous pouvez également créer une variable de structure et attribuer des valeurs en
empdata2 := emp{"Raj", "Building-1, Delhi", 25}
Ici, vous devez conserver l'ordre des éléments. Raj sera mappé au nom, au prochain élément à adresser et au dernier à vieillir.
Exécutez le code ci-dessous
package main import "fmt" //declared the structure named emp type emp struct { name string address string age int } //function which accepts variable of emp type and prints name property func display(e emp) { fmt.Println(e.name) } func main() { // declares a variable, empdata1, of the type emp var empdata1 emp //assign values to members of empdata1 empdata1.name = "John" empdata1.address = "Street-1, London" empdata1.age = 30 //declares and assign values to variable empdata2 of type emp empdata2 := emp{"Raj", "Building-1, Paris", 25} //prints the member name of empdata1 and empdata2 using display function display(empdata1) display(empdata2) }
Sortie
John Raj
Méthodes (pas de fonctions)
Une méthode est une fonction avec un argument récepteur. ArchiTechniquement, c'est entre le mot-clé func et le nom de la méthode. La syntaxe d'une méthode est
func (variable variabletype) methodName(parameter1 paramether1type) { }
Convertissons l'exemple de programme ci-dessus pour utiliser des méthodes au lieu de fonctions.
package main import "fmt" //declared the structure named emp type emp struct { name string address string age int } //Declaring a function with receiver of the type emp func(e emp) display() { fmt.Println(e.name) } func main() { //declaring a variable of type emp var empdata1 emp //Assign values to members empdata1.name = "John" empdata1.address = "Street-1, Lodon" empdata1.age = 30 //declaring a variable of type emp and assign values to members empdata2 := emp { "Raj", "Building-1, Paris", 25} //Invoking the method using the receiver of the type emp // syntax is variable.methodname() empdata1.display() empdata2.display() }
Go n'est pas un langage orienté objet et il n'a pas de concept de classe. Les méthodes donnent une idée de ce que vous faites dans les programmes orientés objet où les fonctions d'une classe sont invoquées à l'aide de la syntaxe objectname.functionname().
Concurrency
Go prend en charge l'exécution simultanée de tâches. Cela signifie que Go peut exécuter plusieurs tâches simultanément. C’est différent du concept de parallélisme. En parallélisme, une tâche est divisée en petites sous-tâches et exécutées en parallèle. Mais en simultanéité, plusieurs tâches sont exécutées simultanément. La concurrence est obtenue dans Go à l'aide de Goroutines et de canaux.
Goroutines
Une goroutine est une fonction qui peut s'exécuter simultanément avec d'autres fonctions. Habituellement, lorsqu'une fonction est invoquée, le contrôle est transféré dans la fonction appelée et, une fois son exécution terminée, le contrôle revient à la fonction appelante. La fonction appelante poursuit alors son exécution. La fonction appelante attend que la fonction invoquée termine l'exécution avant de poursuivre le reste des instructions.
Mais dans le cas de goroutine, la fonction appelante n'attendra pas la fin de l'exécution de la fonction invoquée. Il continuera à s'exécuter avec les prochaines instructions. Vous pouvez avoir plusieurs goroutines dans un programme.
De plus, le programme principal se fermera une fois l'exécution de ses instructions terminée et il n'attendra pas la fin des goroutines invoquées.
Goroutine est invoqué à l'aide du mot-clé go suivi d'un appel de fonction.
Exemple
go add(x,y)
Vous comprendrez les goroutines avec les exemples Golang ci-dessous. Exécutez le programme ci-dessous
package main import "fmt" func display() { for i:=0; i<5; i++ { fmt.Println("In display") } } func main() { //invoking the goroutine display() go display() //The main() continues without waiting for display() for i:=0; i<5; i++ { fmt.Println("In main") } }
La sortie sera
In main In main In main In main In main
Ici, le programme principal a terminé son exécution avant même le démarrage de la goroutine. Le display() est une goroutine qui est invoquée en utilisant la syntaxe
go function_name(parameter list)
Dans le code ci-dessus, main() n'attend pas la fin de display() et main() a terminé son exécution avant que display() n'exécute son code. Ainsi, l’instruction print à l’intérieur de display() n’a pas été imprimée.
Maintenant, nous modifions le programme pour imprimer également les instructions de display(). Nous ajoutons un délai de 2 secondes dans la boucle for de main() et un délai de 1 seconde dans la boucle for de display().
package main import "fmt" import "time" func display() { for i:=0; i<5; i++ { time.Sleep(1 * time.Second) fmt.Println("In display") } } func main() { //invoking the goroutine display() go display() for i:=0; i<5; i++ { time.Sleep(2 * time.Second) fmt.Println("In main") } }
Le résultat sera quelque peu similaire à
In display In main In display In display In main In display In display In main In main In main
Ici, vous pouvez voir que les deux boucles sont exécutées de manière superposée en raison de l'exécution simultanée.
Canaux
Les canaux sont un moyen pour les fonctions de communiquer entre elles. Il peut être considéré comme un support sur lequel une routine place les données et est accessible par une autre routine sur le serveur Golang.
Un canal peut être déclaré avec la syntaxe
channel_variable := make(chan datatype)
Mise en situation :
ch := make(chan int)
Vous pouvez envoyer des données à un canal en utilisant la syntaxe
channel_variable <- variable_name
Exemple
ch <- x
Vous pouvez recevoir des données d'un canal en utilisant la syntaxe
variable_name := <- channel_variable
Exemple
y := <- ch
Dans les exemples de goroutine en langage Go ci-dessus, vous avez vu que le programme principal n'attend pas le goroutine. Mais ce n’est pas le cas lorsqu’il s’agit de chaînes. Supposons que si une goroutine envoie des données vers le canal, main() attendra l'instruction recevant les données du canal jusqu'à ce qu'elle obtienne les données.
Vous le verrez dans les exemples de langage Go ci-dessous. Tout d’abord, écrivez une goroutine normale et voyez le comportement. Modifiez ensuite le programme pour utiliser les canaux et voir le comportement.
Exécutez le programme ci-dessous
package main import "fmt" import "time" func display() { time.Sleep(5 * time.Second) fmt.Println("Inside display()") } func main() { go display() fmt.Println("Inside main()") }
La sortie sera
Inside main()
Le main() a terminé l'exécution et s'est terminé avant que la goroutine ne s'exécute. Ainsi, l'impression à l'intérieur du display() n'a pas été exécutée.
Modifiez maintenant le programme ci-dessus pour utiliser les canaux et voir le comportement.
package main import "fmt" import "time" func display(ch chan int) { time.Sleep(5 * time.Second) fmt.Println("Inside display()") ch <- 1234 } func main() { ch := make(chan int) go display(ch) x := <-ch fmt.Println("Inside main()") fmt.Println("Printing x in main() after taking from channel:",x) }
La sortie sera
Inside display() Inside main() Printing x in main() after taking from channel: 1234
Ici, ce qui se passe, c'est que main() en atteignant x := <-ch attendra les données sur le canal ch. Le display() attend 5 secondes, puis transmet les données au canal ch. Le main() à la réception des données du canal est débloqué et continue son exécution.
L'expéditeur qui transmet les données au canal peut informer les récepteurs qu'aucune donnée ne sera ajoutée au canal en fermant le canal. Ceci est principalement utilisé lorsque vous utilisez une boucle pour transmettre des données vers un canal. Un canal peut être fermé en utilisant
close(channel_name)
Et côté récepteur, il est possible de vérifier si le canal est fermé à l'aide d'une variable supplémentaire tout en récupérant les données du canal en utilisant
variable_name, status := <- channel_variable
Si le statut est True, cela signifie que vous avez reçu des données du canal. Si faux, cela signifie que vous essayez de lire à partir d'un canal fermé
Vous pouvez également utiliser des canaux de communication entre goroutines. Besoin d'utiliser 2 goroutines : l'une transmet les données au canal et l'autre reçoit les données du canal. Voir le programme ci-dessous
package main import "fmt" import "time" //This subroutine pushes numbers 0 to 9 to the channel and closes the channel func add_to_channel(ch chan int) { fmt.Println("Send data") for i:=0; i<10; i++ { ch <- i //pushing data to channel } close(ch) //closing the channel } //This subroutine fetches data from the channel and prints it. func fetch_from_channel(ch chan int) { fmt.Println("Read data") for { //fetch data from channel x, flag := <- ch //flag is true if data is received from the channel //flag is false when the channel is closed if flag == true { fmt.Println(x) }else{ fmt.Println("Empty channel") break } } } func main() { //creating a channel variable to transport integer values ch := make(chan int) //invoking the subroutines to add and fetch from the channel //These routines execute simultaneously go add_to_channel(ch) go fetch_from_channel(ch) //delay is to prevent the exiting of main() before goroutines finish time.Sleep(5 * time.Second) fmt.Println("Inside main()") }
Ici, il y a 2 sous-programmes, l'un pousse les données vers le canal et l'autre imprime les données sur le canal. La fonction add_to_channel ajoute les nombres de 0 à 9 et ferme le canal. Simultanément, la fonction fetch_from_channel attend à
x, flag := <- ch et une fois que les données sont disponibles, il imprime les données. Il se termine une fois que le drapeau est faux, ce qui signifie que le canal est fermé.
L'attente dans main() est donnée pour empêcher la sortie de main() jusqu'à ce que les goroutines terminent l'exécution.
Exécutez le code et voyez le résultat comme
Read data Send data 0 1 2 3 4 5 6 7 8 9 Empty channel Inside main()
Choisir
Select peut être considéré comme une instruction switch qui fonctionne sur les canaux. Ici, les instructions case seront une opération de canal. Habituellement, chaque instruction de cas sera lue par tentative à partir du canal. Lorsqu'un des cas est prêt (le canal est lu), alors l'instruction associée à ce cas est exécutée. Si plusieurs cas sont prêts, il en choisira un au hasard. Vous pouvez avoir un cas par défaut qui est exécuté si aucun des cas n'est prêt.
Voyons le code ci-dessous
package main import "fmt" import "time" //push data to channel with a 4 second delay func data1(ch chan string) { time.Sleep(4 * time.Second) ch <- "from data1()" } //push data to channel with a 2 second delay func data2(ch chan string) { time.Sleep(2 * time.Second) ch <- "from data2()" } func main() { //creating channel variables for transporting string values chan1 := make(chan string) chan2 := make(chan string) //invoking the subroutines with channel variables go data1(chan1) go data2(chan2) //Both case statements wait for data in the chan1 or chan2. //chan2 gets data first since the delay is only 2 sec in data2(). //So the second case will execute and exits the select block select { case x := <-chan1: fmt.Println(x) case y := <-chan2: fmt.Println(y) } }
L'exécution du programme ci-dessus donnera le résultat :
from data2()
Ici, l'instruction select attend que les données soient disponibles dans l'un des canaux. data2() ajoute des données au canal après une veille de 2 secondes, ce qui entraînera l'exécution du deuxième cas.
Ajoutez un cas par défaut à la sélection dans le même programme et voyez le résultat. Ici, en atteignant le bloc de sélection, si aucun cas n'a de données prêtes sur le canal, il exécutera le bloc par défaut sans attendre que les données soient disponibles sur aucun canal.
package main import "fmt" import "time" //push data to channel with a 4 second delay func data1(ch chan string) { time.Sleep(4 * time.Second) ch <- "from data1()" } //push data to channel with a 2 second delay func data2(ch chan string) { time.Sleep(2 * time.Second) ch <- "from data2()" } func main() { //creating channel variables for transporting string values chan1 := make(chan string) chan2 := make(chan string) //invoking the subroutines with channel variables go data1(chan1) go data2(chan2) //Both case statements check for data in chan1 or chan2. //But data is not available (both routines have a delay of 2 and 4 sec) //So the default block will be executed without waiting for data in channels. select { case x := <-chan1: fmt.Println(x) case y := <-chan2: fmt.Println(y) default: fmt.Println("Default case executed") } }
Ce programme donnera le résultat :
Default case executed
En effet, lorsque le bloc de sélection a été atteint, aucun canal n'avait de données à lire. Ainsi, le cas par défaut est exécuté.
mutex
Mutex est la forme abrégée d'exclusion mutuelle. Mutex est utilisé lorsque vous ne souhaitez pas autoriser l'accès à une ressource par plusieurs sous-programmes en même temps. Mutex a 2 méthodes – Verrouiller et Déverrouiller. Mutex est contenu dans le package de synchronisation. Vous devez donc importer le package de synchronisation. Les instructions qui doivent être exécutées mutuellement exclusivement peuvent être placées dans mutex.Lock() et mutex.Unlock().
Apprenons le mutex avec un exemple qui compte le nombre de fois qu'une boucle est exécutée. Dans ce programme, nous nous attendons à ce que la routine exécute la boucle 10 fois et que le décompte soit stocké en somme. Vous appelez cette routine 3 fois pour que le nombre total soit de 30. Le nombre est stocké dans une variable globale count.
Tout d'abord, vous exécutez le programme sans mutex
package main import "fmt" import "time" import "strconv" import "math/rand" //declare count variable, which is accessed by all the routine instances var count = 0 //copies count to temp, do some processing(increment) and store back to count //random delay is added between reading and writing of count variable func process(n int) { //loop incrementing the count by 10 for i := 0; i < 10; i++ { time.Sleep(time.Duration(rand.Int31n(2)) * time.Second) temp := count temp++ time.Sleep(time.Duration(rand.Int31n(2)) * time.Second) count = temp } fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count)) } func main() { //loop calling the process() 3 times for i := 1; i < 4; i++ { go process(i) } //delay to wait for the routines to complete time.Sleep(25 * time.Second) fmt.Println("Final Count:", count) }
Voir le résultat
Count after i=1 Count: 11 Count after i=3 Count: 12 Count after i=2 Count: 13 Final Count: 13
Le résultat pourrait être différent lorsque vous l'exécuterez mais le résultat final ne sera pas 30.
Ici, ce qui se passe, c'est que 3 goroutines tentent d'augmenter le nombre de boucles stockées dans le nombre de variables. Supposons qu'à un moment donné, le compte soit 5 et que goroutine1 va incrémenter le compte à 6. Les principales étapes comprennent
Copier le nombre dans la version temporaire
Augmenter la température
Stocker la température pour compter
Supposons que peu de temps après avoir effectué l'étape 3 de goroutine1 ; une autre goroutine peut avoir une ancienne valeur, par exemple 3 effectue les étapes ci-dessus et stocke 4 en arrière, ce qui est faux. Cela peut être évité en utilisant un mutex qui fait attendre d'autres routines lorsqu'une routine utilise déjà la variable.
Vous allez maintenant exécuter le programme avec mutex. Ici, les 3 étapes mentionnées ci-dessus sont exécutées dans un mutex.
package main import "fmt" import "time" import "sync" import "strconv" import "math/rand" //declare a mutex instance var mu sync.Mutex //declare count variable, which is accessed by all the routine instances var count = 0 //copies count to temp, do some processing(increment) and store back to count //random delay is added between reading and writing of count variable func process(n int) { //loop incrementing the count by 10 for i := 0; i < 10; i++ { time.Sleep(time.Duration(rand.Int31n(2)) * time.Second) //lock starts here mu.Lock() temp := count temp++ time.Sleep(time.Duration(rand.Int31n(2)) * time.Second) count = temp //lock ends here mu.Unlock() } fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count)) } func main() { //loop calling the process() 3 times for i := 1; i < 4; i++ { go process(i) } //delay to wait for the routines to complete time.Sleep(25 * time.Second) fmt.Println("Final Count:", count) }
Maintenant, la sortie sera
Count after i=3 Count: 21 Count after i=2 Count: 28 Count after i=1 Count: 30 Final Count: 30
Ici, nous obtenons le résultat attendu comme résultat final. Parce que les instructions de lecture, d'incrémentation et de réécriture du compte sont exécutées dans un mutex.
La gestion des erreurs
Les erreurs sont des conditions anormales comme la fermeture d'un fichier qui n'est pas ouvert, l'ouverture d'un fichier qui n'existe pas, etc. Les fonctions renvoient généralement des erreurs comme dernière valeur de retour.
L'exemple ci-dessous explique plus en détail l'erreur.
package main import "fmt" import "os" //function accepts a filename and tries to open it. func fileopen(name string) { f, er := os.Open(name) //er will be nil if the file exists else it returns an error object if er != nil { fmt.Println(er) return }else{ fmt.Println("file opened", f.Name()) } } func main() { fileopen("invalid.txt") }
La sortie sera:
open /invalid.txt: no such file or directory
Ici, nous avons essayé d'ouvrir un fichier inexistant et il a renvoyé l'erreur à la variable er. Si le fichier est valide, alors l'erreur sera nulle
Erreurs personnalisées
Grâce à cette fonctionnalité, vous pouvez créer des erreurs personnalisées. Cela se fait en utilisant New() du package d'erreur. Nous allons réécrire le programme ci-dessus pour utiliser les erreurs personnalisées.
Exécutez le programme ci-dessous
package main import "fmt" import "os" import "errors" //function accepts a filename and tries to open it. func fileopen(name string) (string, error) { f, er := os.Open(name) //er will be nil if the file exists else it returns an error object if er != nil { //created a new error object and returns it return "", errors.New("Custom error message: File name is wrong") }else{ return f.Name(),nil } } func main() { //receives custom error or nil after trying to open the file filename, error := fileopen("invalid.txt") if error != nil { fmt.Println(error) }else{ fmt.Println("file opened", filename) } }
La sortie sera:
Custom error message:File name is wrong
Ici, la zone() renvoie l'aire d'un carré. Si l'entrée est inférieure à 1, alors Area() renvoie un message d'erreur.
Lecture de fichiers
Les fichiers sont utilisés pour stocker des données. Go nous permet de lire les données des fichiers
Créez d'abord un fichier, data.txt, dans votre répertoire actuel avec le contenu ci-dessous.
Line one Line two Line three
Exécutez maintenant le programme ci-dessous pour voir qu'il imprime le contenu de l'intégralité du fichier en sortie.
package main import "fmt" import "io/ioutil" func main() { data, err := ioutil.ReadFile("data.txt") if err != nil { fmt.Println("File reading error", err) return } fmt.Println("Contents of file:", string(data)) }
Ici les données, err := ioutil.ReadFile("data.txt") lit les données et renvoie une séquence d'octets. Lors de l'impression, il est converti au format chaîne.
Rédaction de fichiers
Vous verrez cela avec un programme
package main import "fmt" import "os" func main() { f, err := os.Create("file1.txt") if err != nil { fmt.Println(err) return } l, err := f.WriteString("Write Line one") if err != nil { fmt.Println(err) f.Close() return } fmt.Println(l, "bytes written") err = f.Close() if err != nil { fmt.Println(err) return } }
Ici, un fichier est créé, test.txt. Si le fichier existe déjà, son contenu est tronqué. Writeline() est utilisé pour écrire le contenu du fichier. Après cela, vous avez fermé le fichier en utilisant Close().
Aide-mémoire
Dans ce didacticiel Go, nous avons couvert :
Sujet | Description | Syntaxe |
---|---|---|
Types de base | Numérique, chaîne, booléen | |
Variables | Déclarer et attribuer des valeurs aux variables | var type nom_variable var nom_variable type = valeur var nom_variable1, nom_variable2 = valeur1, valeur2 nom_variable := valeur |
Constants | Variables dont la valeur ne peut pas être modifiée une fois attribuée | variable const = valeur |
Pour la boucle | Exécutez des instructions en boucle. | pour initialisation_expression ; expression_évaluation ; expression_itération{ // une ou plusieurs instructions } |
Sinon | C'est une instruction conditionnelle | si condition{ // déclarations_1 }autre{ // déclarations_2 } |
interrupteur | Instruction conditionnelle avec plusieurs cas | changer d'expression { valeur de cas_1 : déclarations_1 valeur de cas_2 : déclarations_2 cas valeur_n : déclarations_n par défaut: déclarations_par défaut } |
tableau | Séquence nommée de taille fixe d'éléments du même type | nom du tableau := [taille] tapez {value_0,value_1,…,value_size-1} |
Tranche | Portion ou segment d'un tableau | var slice_name [] type = array_name[début: fin] |
Les fonctions | Bloc d'instructions qui effectue une tâche spécifique | func nom_fonction (type paramètre_1, type paramètre_n) return_type { //instructions } |
nos différents forfaits | Sont utilisés pour organiser le code. Augmente la lisibilité et la réutilisabilité du code | importer nom_paquet |
Reporter | Diffère l'exécution d'une fonction jusqu'à ce que la fonction contenant termine son exécution | différer nom_fonction (liste_paramètres) |
Pointers | Stocke l'adresse mémoire d'une autre variable. | var nom_variable *type |
Structure | Type de données défini par l'utilisateur qui contient lui-même un élément supplémentaire du même type ou d'un type différent | tapez nom_structure struct { variable_1 variable_1_type variable_2 variable_2_type variable_n variable_n_type } |
Méthodologie | Une méthode est une fonction avec un argument récepteur | func (variable type de variable) nom_méthode (liste_paramètres) { } |
Goroutine | Une fonction qui peut s'exécuter simultanément avec d'autres fonctions. | allez nom_fonction (liste_paramètres) |
Développement | Façon pour les fonctions de communiquer entre elles. Support sur lequel une routine place des données et auquel une autre routine accède. | Déclarer: ch := faire(chan int) Envoyer des données au canal : variable_canal <- nom_variable Recevoir depuis la chaîne : nom_variable := <- variable_canal |
Choisir | Instruction Switch qui fonctionne sur les canaux. Les instructions case seront une opération de canal. Lorsque l'un des canaux est prêt avec des données, l'instruction associée à ce cas est exécutée | sélectionnez { cas x := <-chan1 : fmt.Println(x) cas y := <-chan2 : fmt.Println(y) } |
mutex | Mutex est utilisé lorsque vous ne souhaitez pas autoriser l'accès à une ressource par plusieurs sous-programmes en même temps. Mutex a 2 méthodes – Verrouiller et Déverrouiller | mutex.Lock() //instructions mutex.Unlock(). |
Lire des fichiers | Lit les données et renvoie une séquence d'octets. | Données, erreur := ioutil.ReadFile(filename) |
Écrire le fichier | Écrit des données dans un fichier | l, erreur := f.WriteString(text_to_write) |