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.neohabituellement. Ceci est réalisé à l'aide de canaux, de goroutines, etc. Go Language dispose d'un garbage collection qui effectue lui-même la gestion de 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 un entier, une virgule flottante et complex valeurs. 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 suivantwing code

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 suiviwing code

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

Interrupteur

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 suiviwing .

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 tutoriel Golang, exécutons le suiviwing Exemple Golang. 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

Formules

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 tutoriel Golang, comment créer et utiliser des packages dans la suitewing Exemple Golang.

É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, trouvez 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

Ouvrages d'art

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.neohabituellement. 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.neohabituellement. 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. Simultaneogénéralement, 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()

Sélectionnez

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 sync emballer. Il faut donc importer le sync emballer. 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
}
Formules 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
Sélectionnez 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)