Tutorial Golang: impara il linguaggio di programmazione Go per principianti

Cos'è Go?

Go (noto anche come Golang) è un linguaggio di programmazione open source sviluppato da Google. È un linguaggio compilato tipizzato staticamente. Go supporta la programmazione simultanea, ovvero consente di eseguire più processi contemporaneamente. Ciò si ottiene utilizzando canali, goroutine, ecc. Go Language dispone di garbage collection che a sua volta si occupa della gestione della memoria e consente l'esecuzione posticipata delle funzioni.

Impareremo tutte le basi di Golang in questo tutorial Learn Go Language.

Come scaricare e installare GO

Passo 1) Vai su https://golang.org/dl/. Scarica il binario per il tuo sistema operativo.

Passo 2) Double fare clic sul programma di installazione e fare clic su Esegui.

Passo 3) Fare clic su Avanti

Passo 4) Selezionare la cartella di installazione e fare clic su Avanti.

Passo 5) Fare clic su Fine al termine dell'installazione.

Passo 6) Una volta completata l'installazione puoi verificarla aprendo il terminale e digitando

go version

Verrà visualizzata la versione di go installata

Il tuo programma First Go – Go Hello World!

Crea una cartella chiamata studyGo. In questo tutorial sul linguaggio Go, creeremo i nostri programmi Go all'interno di questa cartella. I file Go vengono creati con l'estensione .andare. È possibile eseguire i programmi Go utilizzando la sintassi

go run <filename>

Crea un file chiamato first.go e aggiungi il codice seguente e salva

package main
import ("fmt")

func main() {
	fmt.Println("Hello World! This is my first Go program\n")
}

Passare a questa cartella nel terminale. Esegui il programma utilizzando il comando

vai a correre prima. vai

È possibile vedere la stampa dell'output

Hello World! This is my first Go program

Ora parliamo del programma di cui sopra.

pacchetto principale: ogni programma Go Language dovrebbe iniziare con il nome di un pacchetto. Go ci consente di utilizzare i pacchetti in altri programmi Go e quindi supporta la riusabilità del codice. L'esecuzione di un programma Go inizia con il codice all'interno del pacchetto denominato main.

import fmt – importa il pacchetto fmt. Questo pacchetto implementa le funzioni di I/O.

func main() – Questa è la funzione da cui inizia l'esecuzione del programma. La funzione principale dovrebbe essere sempre inserita nel pacchetto principale. Sotto main(), puoi scrivere il codice all'interno di { }.

fmt.Println – Questo stamperà il testo sullo schermo tramite la funzione Println di fmt.

Nota: nelle sezioni seguenti di questo tutorial Go, quando menzioni esegui/esegui il codice, significa salvare il codice in un file con estensione .go ed eseguirlo utilizzando la sintassi

    go run <filename>

Tipi di dati

I tipi (tipi di dati) rappresentano il tipo del valore memorizzato in una variabile, il tipo del valore restituito da una funzione, ecc.

Ci sono tre tipi base in Go Language

Tipi numerici – Rappresenta valori numerici che includono valori interi, a virgola mobile e complessi. I vari tipi numerici sono:

int8 – Interi con segno a 8 bit.

int16 – Interi con segno a 16 bit.

int32 – Interi con segno a 32 bit.

int64 – Interi con segno a 64 bit.

uint8 – Interi senza segno a 8 bit.

uint16 – Interi senza segno a 16 bit.

uint32 – Interi senza segno a 32 bit.

uint64 – Interi senza segno a 64 bit.

float32 – Numeri in virgola mobile a 32 bit.

float64 – Numeri in virgola mobile a 64 bit.

complex64 – ha float32 parti reali e immaginarie.

complex128 – ha float32 parti reali e immaginarie.

Tipi di stringa – Rappresenta una sequenza di byte (caratteri). Puoi eseguire varie operazioni sulle stringhe come concatenazione di stringhe, estrazione di sottostringhe, ecc

Tipi booleani – Rappresenta 2 valori, vero o falso.

Interfaccia Golang

Interfaccia Golang è una raccolta di firme di metodo utilizzate da un Type per implementare il comportamento degli oggetti. L'obiettivo principale dell'interfaccia Golang è fornire firme dei metodi con nomi, argomenti e tipi restituiti. Spetta a Type dichiarare e implementare il metodo. Un'interfaccia in Golang può essere dichiarata utilizzando la parola chiave "interfaccia".

Variabili

Le variabili puntano a una posizione di memoria che memorizza un qualche tipo di valore. Il parametro type (nella sintassi seguente) rappresenta il tipo di valore che può essere archiviato nella posizione di memoria.

La variabile può essere dichiarata utilizzando la sintassi

    var <variable_name> <type>

Una volta dichiarata una variabile di un tipo, è possibile assegnare la variabile a qualsiasi valore di quel tipo.

Puoi anche dare un valore iniziale ad una variabile durante la dichiarazione stessa usando

    var <variable_name> <type> = <value>

Se dichiari la variabile con un valore iniziale, vai a dedurre il tipo della variabile dal tipo di valore assegnato. Quindi puoi omettere il tipo durante la dichiarazione usando la sintassi

    var <variable_name> = <value>

Inoltre, puoi dichiarare più variabili con la sintassi

    var <variable_name1>, <variable_name2>  = <value1>, <value2>

Il programma seguente in questo tutorial Go contiene alcuni esempi Golang di dichiarazioni di variabili

 
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)
}

L'output sarà

x: 3
y: 20
z: 50
i and j: 100 hello

Go Language fornisce anche un modo semplice per dichiarare le variabili con valore omettendo la parola chiave var using

    <variable_name> := <value>

Nota che hai usato := invece di =. Non è possibile utilizzare := solo per assegnare un valore a una variabile già dichiarata. := viene utilizzato per dichiarare e assegnare valore.

Crea un file chiamatoassign.go con il seguente codice

package main
import ("fmt")

func main() {
	a := 20
	fmt.Println(a)

	//gives error since a is already declared
	a := 30
	fmt.Println(a)
}

Esegui go runassign.go per vedere il risultato come

./assign.go:7:4: no new variables on left side of :=		

Le variabili dichiarate senza valore iniziale avranno 0 per i tipi numerici, false per i booleani e stringa vuota per le stringhe

Costante

Le variabili costanti sono quelle variabili il cui valore non può essere modificato una volta assegnato. Una costante nel linguaggio di programmazione Go viene dichiarata utilizzando la parola chiave "const"

Crea un file chiamato Constant.go e con il seguente codice

package main
import ("fmt")

func main() {
	const b =10
	fmt.Println(b)
	b = 30
	fmt.Println(b)
}

Esegui go run Constant.go per vedere il risultato come

.constant.go:7:4: cannot assign to b

Per esempi di loop

I cicli vengono utilizzati per eseguire ripetutamente un blocco di istruzioni in base a una condizione. La maggior parte dei linguaggi di programmazione fornisce 3 tipi di cicli: for, while, do while. Ma il linguaggio di programmazione Go supporta solo il ciclo for.

La sintassi di un ciclo Golang for è

for initialisation_expression; evaluation_expression; iteration_expression{
   // one or more statement
}

L'inizializzazione_espressione viene eseguita per prima (e solo una volta) nel ciclo Golang for.

Quindi viene valutata la valutazione_espressione e se è vera viene eseguito il codice all'interno del blocco.

L'id iteration_expression viene eseguito e la valutazione_expression viene valutata nuovamente. Se è vero, il blocco di istruzioni viene eseguito nuovamente. Ciò continuerà finché la valutazione_espressione non diventa falsa.

Copia il programma seguente in un file ed eseguilo per vedere i numeri di stampa del ciclo Golang for da 1 a 5

package main
import "fmt"

func main() {  
var i int
for i = 1; i <= 5; i++ {
fmt.Println(i)
    }
}

L'uscita è

1
2
3
4
5

Se altro

Se altrimenti è un'istruzione condizionale. La sinassi lo è

if condition{
// statements_1
}else{
// statements_2
}

Qui la condizione viene valutata e se è vera verranno eseguite le istruzioni_1, altrimenti verranno eseguite le istruzioni_2.

Puoi usare l'istruzione if anche senza else. Puoi anche aver concatenato le istruzioni if ​​else. I programmi seguenti spiegheranno di più su se altro.

Eseguire il programma seguente. Controlla se un numero, x, è inferiore a 10. In tal caso, stamperà "x è inferiore a 10"

package main
import "fmt"

func main() {  
    var x = 50
    if x < 10 {
        //Executes if x < 10
        fmt.Println("x is less than 10")
    } 
}

Qui poiché il valore di x è maggiore di 10, l'istruzione all'interno della condizione di blocco non verrà eseguita.

Ora guarda il programma qui sotto. In questo tutorial sul linguaggio di programmazione Go, abbiamo un blocco else che verrà eseguito in caso di fallimento della valutazione 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")
    }
}

Questo programma ti darà un output

x is greater than or equals 10

Ora in questo tutorial Go, vedremo un programma con più blocchi if else (concatenati if else). Esegui l'esempio Go riportato di seguito. Controlla se un numero è inferiore a 10 o compreso tra 10 e 90 o superiore a 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")
    }
}

Qui innanzitutto la condizione if controlla se x è inferiore a 10 e non lo è. Quindi controlla la condizione successiva (else if) se è compresa tra 10 e 90, anch'essa falsa. Quindi esegue il blocco nella sezione else che fornisce l'output

x is greater than 90

Interruttore

Switch è un'altra istruzione condizionale. Le istruzioni Switch valutano un'espressione e il risultato viene confrontato con un insieme di valori disponibili (casi). Una volta trovata una corrispondenza, vengono eseguite le istruzioni associate a quella corrispondenza (caso). Se non viene trovata alcuna corrispondenza, non verrà eseguito nulla. Puoi anche aggiungere un caso predefinito allo switch che verrà eseguito se non vengono trovate altre corrispondenze. La sintassi dell'interruttore è

switch expression {
    case value_1:
        statements_1
    case value_2:
        statements_2
    case value_n:
        statements_n
    default:
        statements_default
    }

Qui il valore dell'espressione viene confrontato con i valori in ciascun caso. Una volta trovata una corrispondenza, vengono eseguite le istruzioni associate a quel caso. Se non viene trovata alcuna corrispondenza, vengono eseguite le istruzioni nella sezione predefinita.

Eseguire il programma seguente

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")
    }
}

Otterrai l'output come

Sum is 3		

Cambia il valore di aeb in 3 e il risultato sarà

Printing default

Puoi anche avere più valori in un caso separandoli con una virgola.

Array

L'array rappresenta una dimensione fissa, denominata sequenza di elementi dello stesso tipo. Non è possibile avere un array che contenga sia numeri interi che caratteri. Non è possibile modificare la dimensione di un array una volta definita la dimensione.

La sintassi per dichiarare un array è

var arrayname [size] type

A ogni elemento dell'array può essere assegnato un valore utilizzando la sintassi

arrayname [index] = value

L'indice dell'array inizia da 0 alla taglia 1.

È possibile assegnare valori agli elementi dell'array durante la dichiarazione utilizzando la sintassi

arrayname := [size] type {value_0,value_1,…,value_size-1} 

Puoi anche ignorare il parametro size mentre dichiari l'array con valori sostituendo size con ... e il compilatore troverà la lunghezza dal numero di valori. La sintassi è

arrayname :=  […] type {value_0,value_1,…,value_size-1}

È possibile trovare la lunghezza dell'array utilizzando la sintassi

len(arrayname)

Esegui l'esempio Go riportato di seguito per comprendere l'array

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]) 
}

Uscita

Two
3
[One Two Three]
[1 2 3 4 5]
5

Funzione Golang Slice e Aggiungi

Una porzione è una porzione o un segmento di un array. Oppure è una vista parziale o parziale di un array sottostante a cui punta. Puoi accedere agli elementi di una sezione utilizzando il nome della sezione e il numero di indice proprio come fai in un array. Non è possibile modificare la lunghezza di un array, ma è possibile modificare la dimensione di una sezione.

I contenuti di una sezione sono in realtà i puntatori agli elementi di un array. Significa se modifichi qualsiasi elemento in una sezione, anche il contenuto dell'array sottostante verrà influenzato.

La sintassi per creare una sezione è

var slice_name [] type = array_name[start:end]

Ciò creerà una sezione denominata nome_fetta da un array denominato nome_array con gli elementi nell'indice dall'inizio alla fine-1.

Ora in questo tutorial di Golang, eseguiremo il programma seguente. Il programma creerà una sezione dall'array e la stamperà. Inoltre, puoi vedere che la modifica del contenuto nella sezione modificherà l'array effettivo.

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)
}

Questo stamperà il risultato come

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]

Ci sono alcune funzioni come Golang len, Golang append che puoi applicare alle fette

len(nome_fetta) – restituisce la lunghezza della fetta

aggiungi(nome_fetta, valore_1, valore_2) – L'aggiunta Golang viene utilizzata per aggiungere valore_1 e valore_2 a una sezione esistente.

append(slice_nale1,slice_name2…) – aggiunge slice_name2 a slice_name1

Eseguire il seguente programma.

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)
}

L'output sarà

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]

Il programma crea prima 2 fette e ne stampa la lunghezza. Quindi ha aggiunto una fetta all'altra e quindi ha aggiunto una stringa alla fetta risultante.

funzioni

Una funzione rappresenta un blocco di istruzioni che esegue un compito specifico. Una dichiarazione di funzione ci dice il nome della funzione, il tipo restituito e i parametri di input. La definizione di funzione rappresenta il codice contenuto nella funzione. La sintassi per dichiarare la funzione è

func function_name(parameter_1 type, parameter_n type) return_type {
//statements
}

I parametri e i tipi restituiti sono facoltativi. Inoltre, puoi restituire più valori da una funzione.

Ora in questo tutorial su Golang, eseguiamo il seguente esempio di Golang. Qui la funzione denominata calc accetterà 2 numeri ed eseguirà l'addizione e la sottrazione e restituirà entrambi i valori.

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) 
}

L'output sarà

Sum 25
Diff 5

Pack

I pacchetti vengono utilizzati per organizzare il codice. In un grande progetto non è possibile scrivere il codice in un unico file. Il linguaggio di programmazione Go ci consente di organizzare il codice in diversi pacchetti. Ciò aumenta la leggibilità e la riusabilità del codice. Un programma Go eseguibile dovrebbe contenere un pacchetto denominato main e l'esecuzione del programma inizia dalla funzione denominata main. Puoi importare altri pacchetti nel nostro programma usando la sintassi

import package_name

Vedremo e discuteremo in questo tutorial di Golang, come creare e utilizzare i pacchetti nel seguente esempio di Golang.

Passo 1) Crea un file chiamato package_example.go e aggiungi il codice seguente

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) 
}

Nel programma sopra fmt è un pacchetto che il linguaggio di programmazione Go ci fornisce principalmente per scopi di I/O. Inoltre, puoi vedere un pacchetto denominato calcolo. All'interno di main() puoi vedere una somma dei passi :=calcolo.Do_add(x,y). Significa che stai invocando la funzione Do_add dal calcolo del pacchetto.

Passo 2) Innanzitutto, dovresti creare il calcolo del pacchetto all'interno di una cartella con lo stesso nome nella cartella src di go. Il percorso installato di go può essere trovato dalla variabile PATH.

Per Mac, trova il percorso eseguendo echo $PATH

Quindi il percorso è /usr/local/go

Per Windows, trova il percorso eseguendo echo %GOROOT%

Qui il percorso è C:\Go\

Passo 3) Passare alla cartella src (/usr/local/go/src per Mac e C:\Go\src per Windows). Ora dal codice, il nome del pacchetto è calcolo. Go richiede che il pacchetto venga inserito in una directory con lo stesso nome nella directory src. Crea una directory denominata calcolo nella cartella src.

Passo 4) Crea un file chiamato calc.go (puoi dare qualsiasi nome, ma il nome del pacchetto nel codice è importante. Qui dovrebbe essere calcolo) all'interno della directory di calcolo e aggiungi il codice seguente

package calculation
  
func Do_add(num1 int, num2 int)(int) {
    sum := num1 + num2
    return sum
}

Passo 5) Esegui il comando go install dalla directory di calcolo che compilerà calc.go.

Passo 6) Ora torna a package_example.go ed esegui go run package_example.go. L'output sarà Somma 25.

Tieni presente che il nome della funzione Do_add inizia con una lettera maiuscola. Questo perché in Go se il nome della funzione inizia con una lettera maiuscola significa che altri programmi possono vederlo (accedere) altrimenti altri programmi non possono accedervi. Se il nome della funzione fosse do_add , avresti ricevuto l'errore

non può fare riferimento al nome non esportato CALCULATION.CALC..

Differire e impilare i differiti

Le istruzioni di rinvio vengono utilizzate per rinviare l'esecuzione di una chiamata di funzione finché la funzione che contiene l'istruzione di rinvio non completa l'esecuzione.

Impariamo questo con un esempio:

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()")
}

L'output sarà

Inside the main()
Inside the sample()

Qui l'esecuzione di sample() viene posticipata fino al completamento dell'esecuzione della funzione di inclusione (main()).

Il differimento in pila utilizza più istruzioni di differimento. Supponiamo di avere più istruzioni di differimento all'interno di una funzione. Go inserisce tutte le chiamate di funzione differite in uno stack e, una volta restituita la funzione di inclusione, le funzioni in stack vengono eseguite nel Ordine Last In First Out (LIFO). Puoi vederlo nell'esempio seguente.

Esegui il codice seguente

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)
}

L'output sarà

4
3
2
1			

Qui il codice all'interno di main() viene eseguito per primo, quindi le chiamate di funzione differite vengono eseguite nell'ordine inverso, ovvero 4, 3,2,1.

Puntatori

Prima di spiegare i puntatori, discuteremo innanzitutto dell'operatore "&". L'operatore '&' viene utilizzato per ottenere l'indirizzo di una variabile. Significa che '&a' stamperà l'indirizzo di memoria della variabile a.

In questo tutorial di Golang, eseguiremo il programma seguente per visualizzare il valore di una variabile e l'indirizzo di quella variabile

package main
import "fmt"

func main() {
	a := 20
	fmt.Println("Address:",&a)
	fmt.Println("Value:",a)
}

Il risultato sarà

Address: 0xc000078008
Value: 20

Una variabile puntatore memorizza l'indirizzo di memoria di un'altra variabile. È possibile definire un puntatore utilizzando la sintassi

	var variable_name *type

L'asterisco (*) rappresenta che la variabile è un puntatore. Capirai di più eseguendo il programma seguente

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)}

L'output sarà

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

Strutture

Una struttura è un tipo di dati definito dall'utente che a sua volta contiene un ulteriore elemento dello stesso tipo o di un tipo diverso.

L'utilizzo di una struttura è un processo in 2 fasi.

Innanzitutto, crea (dichiara) un tipo di struttura

In secondo luogo, crea variabili di quel tipo per memorizzare valori.

Le strutture vengono utilizzate principalmente quando si desidera archiviare insieme dati correlati.

Considera un'informazione sui dipendenti che contiene nome, età e indirizzo. Puoi gestirlo in 2 modi

Crea 3 array: un array memorizza i nomi dei dipendenti, uno memorizza l'età e il terzo memorizza l'età.

Dichiara un tipo di struttura con 3 campi: nome, indirizzo ed età. Crea un array di quel tipo di struttura in cui ogni elemento è un oggetto struttura con nome, indirizzo ed età.

Il primo approccio non è efficiente. In questi tipi di scenari, le strutture sono più convenienti.

La sintassi per dichiarare una struttura è

type structname struct {
   variable_1 variable_1_type
   variable_2 variable_2_type
   variable_n variable_n_type
}

Un esempio di dichiarazione di struttura è

type emp struct {
    name string
    address string
    age int
}

Qui viene creato un nuovo tipo definito dall'utente denominato emp. Ora puoi creare variabili del tipo emp usando la sintassi

	var variable_name struct_name

Un esempio è

var empdata1 emp 

È possibile impostare valori per empdata1 come

empdata1.name = "John"
	empdata1.address = "Street-1, Bangalore"
	empdata1.age = 30

Puoi anche creare una variabile di struttura e assegnare valori in base a

empdata2 := emp{"Raj", "Building-1, Delhi", 25}

Qui è necessario mantenere l'ordine degli elementi. Raj verrà mappato con il nome, l'elemento successivo con l'indirizzo e l'ultimo con l'età.

Esegui il codice seguente

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)
}

Uscita

John
Raj

Metodi (non funzioni)

Un metodo è una funzione con un argomento ricevente. Archidal punto di vista tecnico, è tra la parola chiave func e il nome del metodo. La sintassi di un metodo è

func (variable variabletype) methodName(parameter1 paramether1type) {  
}

Convertiamo il programma di esempio precedente per utilizzare metodi anziché funzioni.

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 non è un linguaggio orientato agli oggetti e non ha il concetto di classe. I metodi danno un'idea di ciò che fai nei programmi orientati agli oggetti in cui le funzioni di una classe vengono invocate utilizzando la sintassi nomeoggetto.nomefunzione()

Concorrenza

Go supporta l'esecuzione simultanea di attività. Significa che Go può eseguire più attività contemporaneamente. È diverso dal concetto di parallelismo. Nel parallelismo, un'attività è suddivisa in piccole sottoattività e viene eseguita in parallelo. Ma in simultaneità, più attività vengono eseguite contemporaneamente. La concorrenza viene raggiunta in Go utilizzando Goroutine e canali.

Goroutine

Una goroutine è una funzione che può essere eseguita contemporaneamente ad altre funzioni. Di solito quando viene invocata una funzione il controllo viene trasferito nella funzione chiamata e, una volta completata l'esecuzione, il controllo ritorna alla funzione chiamante. La funzione chiamante continua quindi la sua esecuzione. La funzione chiamante attende che la funzione invocata completi l'esecuzione prima di procedere con il resto delle istruzioni.

Ma nel caso della goroutine, la funzione chiamante non attenderà il completamento dell'esecuzione della funzione invocata. L'esecuzione continuerà con le istruzioni successive. È possibile avere più goroutine in un programma.

Inoltre, il programma principale uscirà una volta completata l'esecuzione delle sue istruzioni e non attenderà il completamento delle goroutine invocate.

Goroutine viene invocata utilizzando la parola chiave go seguita da una chiamata di funzione.

Esempio

go add(x,y)

Comprenderai le goroutine con gli esempi Golang riportati di seguito. Eseguire il programma seguente

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")
	}
}

L'output sarà

In main
In main
In main
In main
In main

Qui il programma principale ha completato l'esecuzione ancor prima dell'avvio della goroutine. Il display() è una goroutine che viene invocata utilizzando la sintassi

go function_name(parameter list)

Nel codice precedente, main() non attende il completamento di display() e main() ha completato la sua esecuzione prima che display() eseguisse il suo codice. Quindi l'istruzione print all'interno di display() non è stata stampata.

Ora modifichiamo il programma per stampare anche le istruzioni da display(). Aggiungiamo un ritardo di 2 secondi nel ciclo for di main() e un ritardo di 1 secondo nel ciclo for di 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")
	}
}

L'output sarà in qualche modo simile a

In display
In main
In display
In display
In main
In display
In display
In main
In main
In main

Qui puoi vedere che entrambi i cicli vengono eseguiti in modo sovrapposto a causa dell'esecuzione simultanea.

Canali

I canali sono un modo in cui le funzioni comunicano tra loro. Può essere pensato come un mezzo in cui una routine inserisce i dati e vi accede un'altra routine nel server Golang.

Un canale può essere dichiarato con la sintassi

channel_variable := make(chan datatype)

Esempio:

	ch := make(chan int)

È possibile inviare dati a un canale utilizzando la sintassi

channel_variable <- variable_name

Esempio

    ch <- x

È possibile ricevere dati da un canale utilizzando la sintassi

    variable_name := <- channel_variable

Esempio

   y := <- ch

Negli esempi di goroutine in linguaggio Go sopra riportati, hai visto che il programma principale non aspetta la goroutine. Ma non è così quando sono coinvolti i canali. Supponiamo che se una goroutine invia i dati al canale, main() attenderà l'istruzione che riceve i dati del canale finché non riceverà i dati.

Lo vedrai negli esempi di lingua Go riportati di seguito. Per prima cosa, scrivi una goroutine normale e osserva il comportamento. Quindi modificare il programma per utilizzare i canali e vedere il comportamento.

Eseguire il programma seguente

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()")
}

L'output sarà

Inside main()

Il main() ha terminato l'esecuzione ed è uscito prima dell'esecuzione della goroutine. Quindi la stampa all'interno del display() non è stata eseguita.

Ora modifica il programma sopra per utilizzare i canali e vedere il comportamento.

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)
}

L'output sarà

Inside display()
Inside main()
Printing x in main() after taking from channel: 1234

Qui ciò che accade è che main() al raggiungimento di x := <-ch attenderà i dati sul canale ch. Il display() ha un'attesa di 5 secondi e quindi invia i dati al canale ch. Il main() alla ricezione dei dati dal canale viene sbloccato e continua la sua esecuzione.

Il mittente che invia i dati al canale può informare i ricevitori che non verranno aggiunti altri dati al canale chiudendo il canale. Viene utilizzato principalmente quando si utilizza un loop per inviare dati a un canale. Un canale può essere chiuso utilizzando

close(channel_name)

E dal lato del ricevitore, è possibile verificare se il canale è chiuso utilizzando una variabile aggiuntiva durante il recupero dei dati dal canale utilizzando

variable_name, status := <- channel_variable

Se lo stato è Vero significa che hai ricevuto dati dal canale. Se falso significa che stai tentando di leggere da un canale chiuso

Puoi anche utilizzare i canali per la comunicazione tra goroutine. È necessario utilizzare 2 goroutine: una invia i dati al canale e l'altra riceve i dati dal canale. Vedi il programma qui sotto

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()")
}

Qui ci sono 2 subroutine, una spinge i dati sul canale e l'altra stampa i dati sul canale. La funzione add_to_channel aggiunge i numeri da 0 a 9 e chiude il canale. Contemporaneamente attende la funzione fetch_from_channel

x, flag := <- ch e una volta che i dati diventano disponibili, stampa i dati. Esce quando il flag è falso, il che significa che il canale è chiuso.

L'attesa in main() viene data per impedire l'uscita di main() finché le goroutine non terminano l'esecuzione.

Esegui il codice e visualizza l'output come

Read data
Send data
0
1
2
3
4
5
6
7
8
9
Empty channel
Inside main()

Seleziona

Select può essere visto come un'istruzione switch che funziona sui canali. Qui le dichiarazioni del caso saranno un'operazione di canale. Di solito, ogni istruzione del caso verrà letta dal canale. Quando uno qualsiasi dei casi è pronto (il canale viene letto), viene eseguita l'istruzione associata a quel caso. Se sono pronti più casi, ne sceglierà uno casuale. È possibile avere un caso predefinito che viene eseguito se nessuno dei casi è pronto.

Vediamo il codice seguente

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'esecuzione del programma sopra riportato darà l'output:

from data2()

Qui l'istruzione select attende che i dati siano disponibili in uno qualsiasi dei canali. Il data2() aggiunge dati al canale dopo una sospensione di 2 secondi che causerà l'esecuzione del secondo caso.

Aggiungi un caso predefinito alla selezione nello stesso programma e guarda l'output. Qui, una volta raggiunto il blocco selezionato, se nessun caso ha dati pronti sul canale, eseguirà il blocco predefinito senza attendere che i dati siano disponibili su qualsiasi canale.

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")
    }
}

Questo programma darà l'output:

Default case executed			

Questo perché quando è stato raggiunto il blocco selezionato, nessun canale aveva dati da leggere. Quindi, viene eseguito il caso predefinito.

mutex

Mutex è la forma abbreviata di mutua esclusione. Mutex viene utilizzato quando non si desidera consentire l'accesso a una risorsa da parte di più subroutine contemporaneamente. Mutex ha 2 metodi: Blocca e Sblocca. Mutex è contenuto nel pacchetto di sincronizzazione. Quindi, devi importare il pacchetto di sincronizzazione. Le istruzioni che devono essere eseguite in modo mutuamente esclusivo possono essere inserite all'interno di mutex.Lock() e mutex.Unlock().

Impariamo il mutex con un esempio che conta il numero di volte in cui viene eseguito un ciclo. In questo programma ci aspettiamo che la routine venga eseguita in loop 10 volte e il conteggio venga memorizzato nella somma. Chiami questa routine 3 volte quindi il conteggio totale dovrebbe essere 30. Il conteggio è memorizzato in una variabile globale count.

Innanzitutto, esegui il programma senza 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)
}

Guarda il risultato

 Count after i=1 Count: 11
Count after i=3 Count: 12
Count after i=2 Count: 13
Final Count: 13

Il risultato potrebbe essere diverso quando lo esegui, ma il risultato finale non sarà 30.

Qui ciò che accade è che 3 goroutine stanno tentando di aumentare il conteggio dei loop memorizzato nella variabile count. Supponiamo che in un momento il conteggio sia 5 e goroutine1 incrementerà il conteggio a 6. I passaggi principali includono

Conteggio copie su temp

Incremento temp

Conservare la temperatura per contare

Supponiamo subito dopo aver eseguito il passaggio 3 con goroutine1; un'altra goroutine potrebbe avere un vecchio valore, ad esempio 3 esegue i passaggi precedenti e memorizza 4, il che è sbagliato. Ciò può essere prevenuto utilizzando mutex che fa sì che altre routine attendano quando una routine sta già utilizzando la variabile.

Ora eseguirai il programma con mutex. Qui i 3 passaggi sopra menzionati vengono eseguiti in 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)
}

Ora l'output sarà

 Count after i=3 Count: 21
Count after i=2 Count: 28
Count after i=1 Count: 30
Final Count: 30

Qui otteniamo il risultato atteso come output finale. Poiché le istruzioni di lettura, incremento e riscrittura del conteggio vengono eseguite in un mutex.

Gestione degli errori

Gli errori sono condizioni anomale come la chiusura di un file che non è aperto, l'apertura di un file che non esiste, ecc. Le funzioni solitamente restituiscono errori come ultimo valore restituito.

L'esempio seguente fornisce ulteriori informazioni sull'errore.

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")
}

L'output sarà:

open /invalid.txt: no such file or directory

Qui abbiamo provato ad aprire un file inesistente e ha restituito l'errore alla variabile er. Se il file è valido, l'errore sarà nullo

Errori personalizzati

Utilizzando questa funzione, puoi creare errori personalizzati. Questo viene fatto utilizzando New() del pacchetto error. Riscriveremo il programma precedente per utilizzare errori personalizzati.

Esegui il programma seguente

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)
    }  
}

L'output sarà:

Custom error message:File name is wrong

Qui area() restituisce l'area di un quadrato. Se l'input è inferiore a 1, area() restituisce un messaggio di errore.

Lettura di file

I file vengono utilizzati per archiviare dati. Go ci consente di leggere i dati dai file

Per prima cosa crea un file, data.txt, nella tua directory attuale con il contenuto seguente.

Line one
Line two
Line three

Ora esegui il programma seguente per vedere che stampa il contenuto dell'intero file come output

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))
}

Qui i dati, err := ioutil.ReadFile(“data.txt”) legge i dati e restituisce una sequenza di byte. Durante la stampa viene convertito in formato stringa.

Scrittura di file

Lo vedrai con un programma

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
    }
}

Qui viene creato un file, test.txt. Se il file esiste già, il contenuto del file verrà troncato. Writeline() viene utilizzato per scrivere il contenuto nel file. Successivamente, hai chiuso il file utilizzando Close().

Cheat Sheet

In questo tutorial di Go abbiamo trattato:

Argomento Descrizione Sintassi
Tipi di base Numerico, stringa, bool
Variabili Dichiarare e assegnare valori alle variabili var nome_variabile tipo
var nome_variabile tipo = valore
var nome_variabile1, nome_variabile2 = valore1, valore2
nome_variabile := valore
Costante Variabili il cui valore non può essere modificato una volta assegnato const variabile = valore
Per Loop Esegue istruzioni in un ciclo. per inizializzazione_espressione; espressione_valutazione; espressione_iterazione{
// una o più istruzioni
}
Se altro È una dichiarazione condizionale se condizione{
// istruzioni_1
Else {}
// istruzioni_2
}
interruttore Dichiarazione condizionale con più casi cambia espressione {
valore caso_1:
dichiarazioni_1
valore caso_2:
dichiarazioni_2
caso valore_n:
dichiarazioni_n
di default:
istruzioni_default
}
Italia Sequenza denominata dimensione fissa di elementi dello stesso tipo nomearray := [dimensione] tipo {valore_0,valore_1,…,valore_dimensione-1}
Taglia Porzione o segmento di un array var nome_fetta [] tipo = nome_array[inizio:fine]
funzioni Blocco di istruzioni che esegue un compito specifico func nome_funzione(tipo parametro_1, tipo parametro_n) tipo_ritorno {
//dichiarazioni
}
Pack Vengono utilizzati per organizzare il codice. Aumenta la leggibilità e la riusabilità del codice importa nome_pacchetto
Differire Rimanda l'esecuzione di una funzione finché la funzione che la contiene non termina l'esecuzione rinviare nome_funzione(elenco_parametri)
Puntatori Memorizza l'indirizzo di memoria di un'altra variabile. var nome_variabile *tipo
Structure Tipo di dati definito dall'utente che a sua volta contiene un ulteriore elemento dello stesso tipo o di un tipo diverso tipo nomestruttura struttura {
variabile_1 variabile_1_tipo
variabile_2 variabile_2_tipo
variabile_n variabile_n_tipo
}
Metodi Un metodo è una funzione con un argomento ricevente funz (variabile tipo variabile) nomemetodo(elenco_parametri) {
}
Goroutine Una funzione che può essere eseguita contemporaneamente ad altre funzioni. vai nome_funzione(elenco_parametri)
canale Modo in cui le funzioni comunicano tra loro. Un mezzo su cui una routine inserisce i dati e a cui accede un'altra routine. Dichiarare:
ch := make(chan int)
Invia dati al canale:
variabile_canale <- nome_variabile
Ricevi dal canale:
nome_variabile := <- variabile_canale
Seleziona Dichiarazione Switch che funziona sui canali. Le dichiarazioni del caso costituiranno un'operazione di canale. Quando uno qualsiasi dei canali è pronto con i dati, viene eseguita l'istruzione associata a quel caso Selezionare {
caso x := <-chan1:
fmt.Println(x)
caso y := <-chan2:
fmt.Println(y)
}
mutex Mutex viene utilizzato quando non si desidera consentire l'accesso a una risorsa da parte di più subroutine contemporaneamente. Mutex ha 2 metodi: Blocca e Sblocca mutex.Lock()
//dichiarazioni
mutex.Unlock().
Leggi i file Legge i dati e restituisce una sequenza di byte. Dati, err := ioutil.ReadFile(nome file)
Scrivi file Scrive i dati in un file l, err := f.WriteString(text_to_write)