Tutorial Golang: Învață limbajul de programare Go pentru începători

Ce este Go?

Go (cunoscut și sub numele de Golang) este un limbaj de programare open source dezvoltat de Google. Este un limbaj compilat tipizat static. Go acceptă programarea concomitentă, adică permite rularea mai multor procese simultan. Acest lucru se realizează folosind canale, goroutine, etc. Go Language are garbage collection care face ea însăși gestionarea memoriei și permite execuția amânată a funcțiilor.

Vom învăța toate elementele de bază ale Golang în acest tutorial Learn Go Language.

Cum să descărcați și să instalați GO

Pas 1) Mergi la pagina https://golang.org/dl/. Descărcați binarul pentru sistemul dvs. de operare.

Pas 2) Double faceți clic pe programul de instalare și faceți clic pe Run.

Pas 3) Faceți clic pe Următorul

Pas 4) Selectați folderul de instalare și faceți clic pe Următorul.

Pas 5) Faceți clic pe Terminare odată ce instalarea este finalizată.

Pas 6) Odată ce instalarea este finalizată, o puteți verifica deschizând terminalul și tastând

go version

Aceasta va afișa versiunea go instalată

Programul First Go – Go Hello World!

Creați un folder numit studyGo. În acest tutorial în limba Go, vom crea programele noastre go în interiorul acestui folder. Fișierele Go sunt create cu extensia .merge. Puteți rula programe Go folosind sintaxa

go run <filename>

Creați un fișier numit first.go și adăugați codul de mai jos în el și salvați

package main
import ("fmt")

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

Navigați la acest folder din terminalul dvs. Rulați programul folosind comanda

du-te fugi mai întâi.du-te

Puteți vedea imprimarea de ieșire

Hello World! This is my first Go program

Acum să discutăm despre programul de mai sus.

pachet principal – Fiecare program Go Language ar trebui să înceapă cu un nume de pachet. Go ne permite să folosim pachete în alte programe go și, prin urmare, acceptă reutilizarea codului. Execuția unui program Go începe cu codul din interiorul pachetului numit main.

import fmt – importă pachetul fmt. Acest pachet implementează funcțiile I/O.

func main() – Aceasta este funcția de la care începe execuția programului. Funcția principală ar trebui să fie întotdeauna plasată în pachetul principal. Sub main(), puteți scrie codul în { }.

fmt.Println – Aceasta va imprima textul pe ecran prin funcția Println a fmt.

Notă: În secțiunile de mai jos ale acestui tutorial Go, când menționați executarea/executarea codului, înseamnă să salvați codul într-un fișier cu extensia .go și să îl rulați folosind sintaxa

    go run <filename>

Tipuri de date

Tipurile (tipurile de date) reprezintă tipul valorii stocate într-o variabilă, tipul valorii returnate de o funcție etc.

Există trei tipuri de bază în Go Language

Tipuri numerice – Reprezintă valori numerice care includ valori întregi, virgulă mobilă și complexe. Diferite tipuri numerice sunt:

int8 – numere întregi cu semn pe 8 biți.

int16 – numere întregi cu semn pe 16 biți.

int32 – numere întregi cu semn pe 32 biți.

int64 – numere întregi cu semn pe 64 biți.

uint8 – numere întregi fără semn pe 8 biți.

uint16 – numere întregi fără semn pe 16 biți.

uint32 – numere întregi fără semn pe 32 biți.

uint64 – numere întregi fără semn pe 64 biți.

float32 – numere în virgulă mobilă pe 32 de biți.

float64 – numere în virgulă mobilă pe 64 de biți.

complex64 – are float32 părți reale și imaginare.

complex128 – are float32 părți reale și imaginare.

Tipuri de șiruri – Reprezintă o secvență de octeți (caractere). Puteți face diverse operații pe șiruri, cum ar fi concatenarea șirurilor, extragerea subșirurilor etc

Tipuri booleene – Reprezintă 2 valori, fie adevărate fie false.

Interfața Golang

Interfața Golang este o colecție de semnături de metodă utilizată de un tip pentru a implementa comportamentul obiectelor. Scopul principal al interfeței Golang este de a oferi semnături de metodă cu nume, argumente și tipuri de returnare. Este la latitudinea unui Type să declare și să implementeze metoda. O interfață în Golang poate fi declarată folosind cuvântul cheie „interfață”.

Variabile

Variabilele indică o locație de memorie care stochează un fel de valoare. Parametrul tip (în sintaxa de mai jos) reprezintă tipul de valoare care poate fi stocată în locația de memorie.

Variabila poate fi declarată folosind sintaxa

    var <variable_name> <type>

Odată ce declarați o variabilă de tip, puteți atribui variabila oricărei valori de tipul respectiv.

De asemenea, puteți da o valoare inițială unei variabile în timpul declarației în sine folosind

    var <variable_name> <type> = <value>

Dacă declarați variabila cu o valoare inițială, mergeți și deduceți tipul variabilei din tipul valorii atribuite. Deci, puteți omite tipul în timpul declarației folosind sintaxa

    var <variable_name> = <value>

De asemenea, puteți declara mai multe variabile cu sintaxa

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

Programul de mai jos din acest tutorial Go are câteva exemple Golang de declarații de variabile

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

Ieșirea va fi

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

Go Language oferă, de asemenea, o modalitate ușoară de a declara variabilele cu valoare prin omiterea cuvântului cheie var folosind

    <variable_name> := <value>

Rețineți că ați folosit := în loc de =. Nu puteți folosi := doar pentru a atribui o valoare unei variabile care este deja declarată. := este folosit pentru a declara și a atribui valoare.

Creați un fișier numit assign.go cu următorul cod

package main
import ("fmt")

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

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

Executați go run assign.go pentru a vedea rezultatul ca

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

Variabilele declarate fără valoare inițială vor avea 0 pentru tipurile numerice, false pentru boolean și șir gol pentru șiruri

constante

Variabilele constante sunt acele variabile a căror valoare nu poate fi modificată odată ce au fost atribuite. O constantă în limbajul de programare Go este declarată prin utilizarea cuvântului cheie „const”

Creați un fișier numit constant.go și cu următorul cod

package main
import ("fmt")

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

Executați go run constant.go pentru a vedea rezultatul ca

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

Pentru exemple de buclă

Buclele sunt folosite pentru a executa un bloc de instrucțiuni în mod repetat pe baza unei condiții. Majoritatea limbajelor de programare oferă 3 tipuri de bucle - for, while, do while. Dar limbajul de programare Go acceptă doar bucla for.

Sintaxa unei bucle pentru Golang este

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

Expresia_inițializare este executată mai întâi (și doar o dată) în Golang pentru bucla.

Apoi evaluation_expression este evaluată și dacă este adevărat, codul din interiorul blocului este executat.

Id-ul expresiei_iterație este executat, iar expresia_evaluării este evaluat din nou. Dacă este adevărat, blocul de instrucțiuni este executat din nou. Aceasta va continua până când expresia_evaluare devine falsă.

Copiați programul de mai jos într-un fișier și executați-l pentru a vedea Golang pentru numere de tipărire în buclă de la 1 la 5

package main
import "fmt"

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

Ieșirea este

1
2
3
4
5

Dacă altceva

Dacă else este o declarație condiționată. Sinaxa este

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

Aici condiția este evaluată și dacă este adevărată instrucțiunile_1 vor fi executate, altfel vor fi executate instrucțiunile_2.

Puteți folosi declarația if și fără else. De asemenea, puteți avea declarații if else înlănțuite. Programele de mai jos vor explica mai multe despre dacă altfel.

Executați programul de mai jos. Verifică dacă un număr, x, este mai mic de 10. Dacă da, va afișa „x este mai mic de 10”

package main
import "fmt"

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

Aici, deoarece valoarea lui x este mai mare decât 10, instrucțiunea din interiorul dacă condiția blocului nu va fi executată.

Acum vedeți programul de mai jos. În acest tutorial despre limbajul de programare Go, avem un bloc else care va fi executat la eșecul evaluării 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")
    }
}

Acest program vă va oferi rezultate

x is greater than or equals 10

Acum, în acest tutorial Go, vom vedea un program cu mai multe blocuri if else (înlănțuite dacă else). Executați exemplul Go de mai jos. Verifică dacă un număr este mai mic de 10 sau este între 10-90 sau mai mare de 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")
    }
}

Aici, mai întâi, condiția if verifică dacă x este mai mic de 10 și nu este. Deci verifică următoarea condiție (altfel dacă) dacă este între 10 și 90, ceea ce este, de asemenea, fals. Deci apoi execută blocul din secțiunea else care dă rezultatul

x is greater than 90

Intrerupator

Switch este o altă declarație condiționată. Instrucțiunile Switch evaluează o expresie și rezultatul este comparat cu un set de valori disponibile (cazuri). Odată ce o potrivire este găsită, instrucțiunile asociate cu acea potrivire (caz) sunt executate. Dacă nu se găsește nicio potrivire, nimic nu va fi executat. De asemenea, puteți adăuga un caz implicit pentru comutare, care va fi executat dacă nu sunt găsite alte potriviri. Sintaxa comutatorului este

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

Aici valoarea expresiei este comparată cu valorile din fiecare caz. Odată ce este găsită o potrivire, instrucțiunile asociate cu acel caz sunt executate. Dacă nu se găsește nicio potrivire, instrucțiunile din secțiunea implicită sunt executate.

Executați programul de mai jos

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

Veți obține rezultatul ca

Sum is 3		

Schimbați valoarea lui a și b la 3 și rezultatul va fi

Printing default

De asemenea, puteți avea mai multe valori într-un caz, separându-le cu virgulă.

Arrays

Array reprezintă o dimensiune fixă, numită secvență de elemente de același tip. Nu puteți avea o matrice care conține atât numere întregi, cât și caractere. Nu puteți modifica dimensiunea unei matrice odată ce ați definit dimensiunea.

Sintaxa pentru declararea unui tablou este

var arrayname [size] type

Fiecărui element de matrice i se poate atribui o valoare folosind sintaxa

arrayname [index] = value

Indexul matricei începe de la 0 la mărimea-1.

Puteți atribui valori elementelor de matrice în timpul declarației folosind sintaxa

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

De asemenea, puteți ignora parametrul dimensiune în timp ce declarați matricea cu valori, înlocuind dimensiunea cu ... iar compilatorul va găsi lungimea din numărul de valori. Sintaxa este

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

Puteți găsi lungimea matricei folosind sintaxa

len(arrayname)

Executați exemplul Go de mai jos pentru a înțelege matricea

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

producție

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

Golang Slice și funcție de adăugare

O felie este o porțiune sau un segment dintr-o matrice. Sau este o vedere sau o vedere parțială a unui tablou subiacent către care indică. Puteți accesa elementele unei secțiuni folosind numele și numărul de index la fel ca într-o matrice. Nu puteți modifica lungimea unei matrice, dar puteți modifica dimensiunea unei felii.

Conținutul unei felii sunt de fapt pointerii către elementele unui tablou. Inseamna dacă modificați orice element dintr-o felie, conținutul matricei de bază va fi de asemenea afectat.

Sintaxa pentru crearea unei felii este

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

Aceasta va crea o porțiune numită slice_name dintr-o matrice numită array_name cu elementele de la index de la început la sfârșit-1.

Acum, în acest tutorial Golang, vom executa programul de mai jos. Programul va crea o porțiune din matrice și o va imprima. De asemenea, puteți vedea că modificarea conținutului din felie va modifica matricea reală.

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

Acesta va imprima rezultatul ca

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]

Există anumite funcții precum Golang len, Golang append pe care le puteți aplica pe felii

len(nume_slice) – returnează lungimea feliei

append(nume_slice, valoare_1, valoare_2) – Golang append este folosit pentru a adăuga value_1 și value_2 la o porțiune existentă.

append(slice_nale1,slice_name2...) – adaugă slice_name2 la slice_name1

Executați următorul program.

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

Ieșirea va fi

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]

Programul creează mai întâi 2 felii și i-a imprimat lungimea. Apoi a atașat o felie la alta și apoi a atașat un șir la felia rezultată.

funcţii

O funcție reprezintă un bloc de instrucțiuni care îndeplinește o anumită sarcină. O declarație de funcție ne spune numele funcției, tipul de returnare și parametrii de intrare. Definiția funcției reprezintă codul conținut în funcție. Sintaxa pentru declararea funcției este

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

Parametrii și tipurile de returnare sunt opționale. De asemenea, puteți returna mai multe valori dintr-o funcție.

Acum, în acest tutorial Golang, să rulăm următorul exemplu Golang. Aici funcția numită calc va accepta 2 numere și efectuează adunarea și scăderea și returnează ambele 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) 
}

Ieșirea va fi

Sum 25
Diff 5

Pachete

Pachetele sunt folosite pentru a organiza codul. Într-un proiect mare, nu este fezabil să scrieți cod într-un singur fișier. Limbajul de programare Go ne permite să organizăm codul în diferite pachete. Acest lucru crește lizibilitatea și reutilizarea codului. Un program Go executabil ar trebui să conțină un pachet numit main și execuția programului începe de la funcția numită main. Puteți importa alte pachete în programul nostru folosind sintaxa

import package_name

Vom vedea și discuta în acest tutorial Golang, cum să creați și să utilizați pachete în următorul exemplu Golang.

Pas 1) Creați un fișier numit package_example.go și adăugați codul de mai jos

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

În programul de mai sus, fmt este un pachet pe care limbajul de programare Go ni-l oferă în principal pentru scopuri I/O. De asemenea, puteți vedea un pachet numit calcul. În interiorul main() puteți vedea o sumă pasă := calcul.Do_add(x,y). Înseamnă că invocați funcția Do_add din calculul pachetului.

Pas 2) În primul rând, ar trebui să creați calculul pachetului într-un folder cu același nume sub folderul src din go. Calea instalată a go poate fi găsită din variabila PATH.

Pentru Mac, găsiți calea executând echo $PATH

Deci calea este /usr/local/go

Pentru Windows, găsiți calea executând echo %GOROOT%

Aici calea este C:\Go\

Pas 3) Navigați la folderul src (/usr/local/go/src pentru Mac și C:\Go\src pentru Windows). Acum, din cod, numele pachetului este de calcul. Go necesită ca pachetul să fie plasat într-un director cu același nume sub directorul src. Creați un director numit calcul în folderul src.

Pas 4) Creați un fișier numit calc.go (puteți da orice nume, dar numele pachetului din cod contează. Aici ar trebui să fie calcul) în directorul de calcul și adăugați codul de mai jos

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

Pas 5) Rulați comanda go install din directorul de calcul care va compila calc.go.

Pas 6) Acum reveniți la package_example.go și rulați go run package_example.go. Ieșirea va fi Suma 25.

Rețineți că numele funcției Do_add începe cu o literă majusculă. Acest lucru se datorează faptului că în Go, dacă numele funcției începe cu o literă majusculă, înseamnă că alte programe îl pot vedea (accesa), altfel alte programe nu îl pot accesa. Dacă numele funcției a fost do_add , atunci ați fi primit eroarea

nu se poate face referire la calculul numelui neexportat.calc..

Amânare și stivuire amână

Instrucțiunile Defer sunt folosite pentru a amâna execuția unui apel de funcție până când funcția care conține instrucțiunea defer finalizează execuția.

Să învățăm asta cu un exemplu:

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

Ieșirea va fi

Inside the main()
Inside the sample()

Aici execuția sample() este amânată până când se finalizează execuția funcției incluse(main()).

Stacking defer folosește mai multe instrucțiuni defer. Să presupunem că aveți mai multe instrucțiuni defer în interiorul unei funcții. Go plasează toate apelurile de funcții amânate într-o stivă și, odată ce funcția de încadrare revine, funcțiile stivuite sunt executate în Comanda Last In First Out (LIFO). Puteți vedea acest lucru în exemplul de mai jos.

Executați codul de mai jos

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

Ieșirea va fi

4
3
2
1			

Aici codul din main() se execută mai întâi, iar apoi apelurile de funcții amânate sunt executate în ordine inversă, adică 4, 3,2,1.

Pointeri

Înainte de a explica indicatorii, să discutăm mai întâi despre operatorul „&”. Operatorul „&” este folosit pentru a obține adresa unei variabile. Înseamnă că „&a” va tipări adresa de memorie a variabilei a.

În acest tutorial Golang, vom executa programul de mai jos pentru a afișa valoarea unei variabile și adresa acelei variabile

package main
import "fmt"

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

Rezultatul va fi

Address: 0xc000078008
Value: 20

O variabilă pointer stochează adresa de memorie a altei variabile. Puteți defini un pointer folosind sintaxa

	var variable_name *type

Asteriscul(*) reprezintă variabila este un pointer. Veți înțelege mai multe executând programul de mai jos

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

Ieșirea va fi

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

Structuri

O structură este un tip de date definit de utilizator, care conține încă un element de același tip sau diferit.

Utilizarea unei structuri este un proces în 2 etape.

Mai întâi, creați (declarați) un tip de structură

În al doilea rând, creați variabile de acest tip pentru a stoca valori.

Structurile sunt utilizate în principal atunci când doriți să stocați împreună date asociate.

Luați în considerare o informație despre angajați care are numele, vârsta și adresa. Puteți gestiona asta în 2 moduri

Creați 3 matrice - o matrice stochează numele angajaților, una stochează vârsta și al treilea stochează vârsta.

Declarați un tip de structură cu 3 câmpuri - nume, adresă și vârstă. Creați o matrice de acel tip de structură în care fiecare element este un obiect de structură având nume, adresă și vârstă.

Prima abordare nu este eficientă. În astfel de scenarii, structurile sunt mai convenabile.

Sintaxa pentru declararea unei structuri este

type structname struct {
   variable_1 variable_1_type
   variable_2 variable_2_type
   variable_n variable_n_type
}

Un exemplu de declarație de structură este

type emp struct {
    name string
    address string
    age int
}

Aici este creat un nou tip definit de utilizator numit emp. Acum, puteți crea variabile de tip emp folosind sintaxa

	var variable_name struct_name

Un exemplu este

var empdata1 emp 

Puteți seta valori pentru empdata1 ca

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

De asemenea, puteți crea o variabilă de structură și aloca valori prin

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

Aici, trebuie să mențineți ordinea elementelor. Raj va fi mapat după nume, următorul element de adresat și ultimul pentru vârsta.

Executați codul de mai jos

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

producție

John
Raj

Metode (nu funcții)

O metodă este o funcție cu un argument receptor. Archidin punct de vedere tehnic, este între cuvântul cheie func și numele metodei. Sintaxa unei metode este

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

Să convertim exemplul de program de mai sus pentru a folosi metode în loc de funcție.

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 nu este un limbaj orientat pe obiecte și nu are conceptul de clasă. Metodele oferă o imagine a ceea ce faci în programele orientate pe obiecte în care funcțiile unei clase sunt invocate folosind sintaxa nume obiect.numefuncție()

Concurenta

Go acceptă executarea concomitentă a sarcinilor. Înseamnă că Go poate executa mai multe sarcini simultan. Este diferit de conceptul de paralelism. În paralelism, o sarcină este împărțită în subsarcini mici și sunt executate în paralel. Dar în concurență, mai multe sarcini sunt executate simultan. Concurența se realizează în Go folosind Goroutines și Canale.

Goroutine

O goroutine este o funcție care poate rula concomitent cu alte funcții. De obicei, atunci când o funcție este invocată, controlul este transferat în funcția apelată și, odată finalizată, controlul de execuție revine la funcția de apelare. Funcția de apelare își continuă apoi execuția. Funcția apelantă așteaptă ca funcția invocată să finalizeze execuția înainte de a continua cu restul instrucțiunilor.

Dar în cazul goroutine, funcția de apelare nu va aștepta finalizarea execuției funcției invocate. Va continua să se execute cu următoarele instrucțiuni. Puteți avea mai multe goroutine într-un program.

De asemenea, programul principal se va închide odată ce își încheie executarea instrucțiunilor și nu va aștepta finalizarea goroutinelor invocate.

Goroutine este invocată folosind cuvântul cheie go urmat de un apel de funcție.

Exemplu

go add(x,y)

Veți înțelege goroutine cu exemplele Golang de mai jos. Executați programul de mai jos

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

Ieșirea va fi

In main
In main
In main
In main
In main

Aici programul principal și-a încheiat execuția chiar înainte de a începe goroutine. Display() este o rutină care este invocată folosind sintaxa

go function_name(parameter list)

În codul de mai sus, main() nu așteaptă ca display() să se finalizeze, iar main() și-a încheiat execuția înainte ca display() să-și execute codul. Deci declarația print din display() nu a fost tipărită.

Acum modificăm programul pentru a imprima instrucțiunile și din display(). Adăugăm o întârziere de 2 secunde în bucla for a main() și o întârziere de 1 secundă în bucla for a afișajului().

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

Ieșirea va fi oarecum similară cu

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

Aici puteți vedea că ambele bucle sunt executate într-un mod suprapus din cauza execuției concurente.

Canale

Canalele sunt o modalitate prin care funcțiile pot comunica între ele. Poate fi considerat ca un mediu în care o rutină plasează date și este accesată de o altă rutină pe serverul Golang.

Un canal poate fi declarat cu sintaxa

channel_variable := make(chan datatype)

Exemplu:

	ch := make(chan int)

Puteți trimite date către un canal folosind sintaxa

channel_variable <- variable_name

Exemplu

    ch <- x

Puteți primi date de la un canal folosind sintaxa

    variable_name := <- channel_variable

Exemplu

   y := <- ch

În exemplele de limbă Go de mai sus de goroutine, ați văzut că programul principal nu așteaptă goroutine. Dar nu este cazul când sunt implicate canale. Să presupunem că dacă o rutină împinge date către canal, main() va aștepta instrucțiunea care primește datele canalului până când primește datele.

Veți vedea acest lucru în exemplele de limbă Go de mai jos. Mai întâi, scrieți o rutină normală și vedeți comportamentul. Apoi modificați programul pentru a utiliza canale și a vedea comportamentul.

Executați programul de mai jos

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

Ieșirea va fi

Inside main()

Main() a terminat execuția și a ieșit înainte ca goroutine să se execute. Deci tipărirea din interiorul display() nu a fost executată.

Acum modificați programul de mai sus pentru a utiliza canalele și a vedea comportamentul.

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

Ieșirea va fi

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

Aici ceea ce se întâmplă este main() la atingerea x := <-ch va aștepta date pe canalul ch. Display-ul () are o așteptare de 5 secunde și apoi împinge datele către canalul de canal. Main() la primirea datelor de la canal este deblocat și își continuă execuția.

Expeditorul care transmite date pe canal poate informa receptorii că nu vor mai fi adăugate date la canal prin închiderea canalului. Acesta este folosit în principal atunci când utilizați o buclă pentru a împinge date către un canal. Un canal poate fi închis folosind

close(channel_name)

Și la capătul receptorului, este posibil să verificați dacă canalul este închis folosind o variabilă suplimentară în timp ce preluați date de pe canal folosind

variable_name, status := <- channel_variable

Dacă starea este True înseamnă că ați primit date de la canal. Dacă este fals, înseamnă că încercați să citiți de pe un canal închis

De asemenea, puteți utiliza canale pentru comunicarea între goroutine. Trebuie să utilizați 2 goroutine - unul împinge datele către canal, iar celălalt primește datele de la canal. Vezi programul de mai jos

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

Aici există 2 subrutine, una împinge datele către canal și cealaltă imprimă datele către canal. Funcția add_to_channel adaugă numerele de la 0 la 9 și închide canalul. Simultan, funcția fetch_from_channel așteaptă la

x, flag := <- ch și odată ce datele devin disponibile, se tipărește datele. Iese odată ce steagul este fals, ceea ce înseamnă că canalul este închis.

Așteptarea în main() este dată pentru a preveni ieșirea din main() până când goroutines termină execuția.

Executați codul și vedeți rezultatul ca

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

Selectați

Select poate fi văzut ca o declarație de comutare care funcționează pe canale. Aici declarațiile de caz vor fi o operațiune de canal. De obicei, fiecare declarație de caz va fi citită încercare de pe canal. Când oricare dintre cazuri este gata (canalul este citit), atunci instrucțiunea asociată cu acel caz este executată. Dacă sunt pregătite mai multe cazuri, va alege unul aleatoriu. Puteți avea un caz implicit care este executat dacă niciunul dintre cazuri nu este pregătit.

Să vedem codul de mai jos

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

Executarea programului de mai sus va da rezultatul:

from data2()

Aici declarația select așteaptă ca datele să fie disponibile pe oricare dintre canale. Data2() adaugă date la canal după un somn de 2 secunde, ceea ce va determina executarea celui de-al doilea caz.

Adăugați un caz implicit la selectare în același program și vedeți rezultatul. Aici, la atingerea blocului select, dacă niciun caz nu are date pregătite pe canal, acesta va executa blocul implicit fără a aștepta ca datele să fie disponibile pe orice 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")
    }
}

Acest program va da rezultatul:

Default case executed			

Acest lucru se datorează faptului că, atunci când a ajuns blocul de selectare, niciun canal nu avea date de citit. Deci, cazul implicit este executat.

mutex

Mutex este forma scurtă pentru excluderea reciprocă. Mutex este folosit atunci când nu doriți să permiteți accesarea unei resurse de mai multe subrutine în același timp. Mutex are 2 metode – Blocare și deblocare. Mutex este conținut în pachetul de sincronizare. Deci, trebuie să importați pachetul de sincronizare. Instrucțiunile care trebuie să fie executate reciproc exclusiv pot fi plasate în interiorul mutex.Lock() și mutex.Unlock().

Să învățăm mutex cu un exemplu care numără de câte ori este executată o buclă. În acest program ne așteptăm ca rutina să ruleze bucla de 10 ori și numărul este stocat în sumă. Apelați această rutină de 3 ori, astfel încât numărul total ar trebui să fie 30. Numărul este stocat într-un număr global de variabile.

În primul rând, rulați programul fără 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)
}

Vezi rezultatul

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

Rezultatul poate fi diferit atunci când îl executați, dar rezultatul final nu va fi 30.

Aici ceea ce se întâmplă este că 3 goroutine încearcă să mărească numărul de bucle stocat în numărul de variabile. Să presupunem că la un moment dat numărul este 5 și goroutine1 va crește numărul la 6. Pașii principali includ

Număr de copii la temp

Creșteți temperatura

Stocați temperatura înapoi pentru a număra

Să presupunem că la scurt timp după efectuarea pasului 3 de către goroutine1; o altă goroutină ar putea avea o valoare veche, să spunem că 3 face pașii de mai sus și stochează 4 înapoi, ceea ce este greșit. Acest lucru poate fi prevenit prin utilizarea mutex care face ca alte rutine să aștepte atunci când o rutină folosește deja variabila.

Acum veți rula programul cu mutex. Aici cei 3 pași menționați mai sus sunt executați într-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)
}

Acum ieșirea va fi

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

Aici obținem rezultatul așteptat ca rezultat final. Deoarece instrucțiunile de citire, creștere și rescriere a numărului sunt executate într-un mutex.

Eroare de manipulare

Erorile sunt condiții anormale, cum ar fi închiderea unui fișier care nu este deschis, deschiderea unui fișier care nu există, etc. Funcțiile returnează de obicei erori ca ultima valoare returnată.

Exemplul de mai jos explică mai multe despre eroare.

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

Ieșirea va fi:

open /invalid.txt: no such file or directory

Aici am încercat să deschidem un fișier inexistent și a returnat eroarea variabilei. Dacă fișierul este valid, atunci eroarea va fi nulă

Erori personalizate

Folosind această funcție, puteți crea erori personalizate. Acest lucru se face prin utilizarea New() a pachetului de erori. Vom rescrie programul de mai sus pentru a folosi erorile personalizate.

Rulați programul de mai jos

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

Ieșirea va fi:

Custom error message:File name is wrong

Aici area() returnează aria unui pătrat. Dacă intrarea este mai mică de 1, atunci area() returnează un mesaj de eroare.

Citirea fișierelor

Fișierele sunt folosite pentru stocarea datelor. Go ne permite să citim date din fișiere

Mai întâi creați un fișier, data.txt, în directorul dvs. actual cu conținutul de mai jos.

Line one
Line two
Line three

Acum rulați programul de mai jos pentru a vedea că imprimă conținutul întregului fișier ca rezultat

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

Aici datele, err := ioutil.ReadFile(“data.txt”) citește datele și returnează o secvență de octeți. În timpul imprimării, acesta este convertit în format șir.

Scrierea fișierelor

Veți vedea asta cu un program

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

Aici este creat un fișier, test.txt. Dacă fișierul există deja, atunci conținutul fișierului este trunchiat. Writeline() este folosit pentru a scrie conținutul fișierului. După aceea, ați închis fișierul folosind Close().

Cheat Sheet

În acest tutorial Go, am acoperit,

Subiect Descriere Sintaxă
Tipuri de bază Numeric, șir, bool
Variabile Declarați și atribuiți valori variabilelor var tipul nume_variabilă
var nume_variabilă tip = valoare
var nume_variabilă1, nume_variabilă2 = valoare1, valoare2
nume_variabilă := valoare
constante Variabile a căror valoare nu poate fi modificată odată ce au fost atribuite const variabilă = valoare
Pentru Loop Executați instrucțiuni într-o buclă. pentru initialisation_expression; expresie_evaluare; expresie_iterație{
// una sau mai multe declarații
}
Dacă altceva Este o declarație condiționată dacă condiția{
// declarații_1
} else {
// declarații_2
}
comuta Declarație condiționată cu mai multe cazuri schimba expresia {
valoarea cazului_1:
declarații_1
valoarea cazului_2:
declarații_2
case value_n:
declarații_n
implicit:
statements_default
}
Mulțime Secvență de nume de dimensiune fixă ​​a elementelor de același tip arrayname := [dimensiune] tip {value_0,value_1,…,value_size-1}
Felie Porțiune sau segment dintr-o matrice var slice_name [] type = array_name[start:end]
funcţii Bloc de declarații care îndeplinește o anumită sarcină func function_name(parameter_1 type, parameter_n type) return_type {
//instrucțiuni
}
Pachete Sunt folosite pentru a organiza codul. Mărește lizibilitatea și reutilizarea codului import package_nam
Amâna Amână execuția unei funcții până când funcția care o conține termină execuția defer function_name(lista_parametri)
Pointeri Stochează adresa de memorie a unei alte variabile. var nume_variabilă *tip
Structure Tip de date definit de utilizator, care conține încă un element de același tip sau diferit tip structname struct {
variabilă_1 variabilă_1_tip
variabilă_2 variabilă_2_tip
variabilă_n variabilă_n_tip
}
Aplicate O metodă este o funcție cu un argument receptor func (variabilă tip variabilă) methodName(lista_parametri) {
}
Goroutine O funcție care poate rula concomitent cu alte funcții. mergeți numele_funcției(lista_parametrilor)
Canal Modul prin care funcțiile pot comunica între ele. Un mediu pe care o rutină plasează date și este accesată de o altă rutină. Declara:
ch := make(chan int)
Trimiteți date către canal:
variabilă_canal <- nume_variabilă
Primiți de pe canal:
nume_variabilă := <- variabilă_canal
Selectați Declarație Switch care funcționează pe canale. Declarațiile de caz vor fi o operațiune de canal. Când oricare dintre canale este gata cu date, atunci instrucțiunea asociată cu acel caz este executată Selectați {
caz x := <-chan1:
fmt.Println(x)
cazul y := <-chan2:
fmt.Println(y)
}
mutex Mutex este folosit atunci când nu doriți să permiteți accesarea unei resurse de mai multe subrutine în același timp. Mutex are 2 metode – Blocare și deblocare mutex.Lock()
//instrucțiuni
mutex.Unlock().
Citiți fișiere Citește datele și returnează o secvență de octeți. Date, err := ioutil.ReadFile(nume fișier)
Scrieți fișierul Scrie date într-un fișier l, err := f.WriteString(text_to_write)