# Info

Bu repository Go dilini tez öyrənmək istəyənlər üçün hazırlanmışdır. Ümumilikdə mövzuları nümunələrlə izah edərək dilin əsas strukturlarını əhatə edir. Eyni zamanda, Go dilini öyrənmək istəyənlər üçün müntəzəm resurs təmin etmək məqsədi daşıyır. Hər bir mövzu təsviri və başa düşülən şəkildə əhatə olunur ki, oxucular Go dilini asanlıqla və tez öyrənə bilsinlər.

Repository-ni bəyənirsinizsə, ulduz qoyub sosial media hesablarınızda paylaşa bilərsiniz ki, daha çox insana çatsın ⭐️.

### **Bu Go nədir?**

Golang (digər adı Go) 2007-ci ildən Google tərəfindən hazırlanmış açıq mənbəli proqramlaşdırma dilidir. O, əsasən alt sistem proqramlaşdırması üçün nəzərdə tutulmuşdur və tərtib edilə bilən və statik olaraq yazılmış dildir. İlk versiya 2009-cu ilin noyabrında buraxıldı. Onun tərtibçisi "gc" (Go Compiler) bir çox əməliyyat sistemi üçün açıq mənbə kimi işlənib hazırlanmışdır.

Golang ilk dəfə Google mühəndisləri Robert Griesemer, Rob Pike və Ken Thompson tərəfindən eksperimental məqsədlər üçün yaradılmışdır. Tənqid olunan problemlərin digər dillərdə həllini təmin etmək və onların yaxşı tərəflərini qorumaq üçün yaradılmışdır.

Go sadə, sürətli və etibarlı proqram təminatı hazırlamaq üçün nəzərdə tutulmuş açıq mənbəli proqramlaşdırma dilidir. Go dili sürətli tərtib prosesi, yüngül sintaksis strukturu və effektiv garbage collection sistemi ilə seçilir.

Go dilinin istifadəsinə müxtəlif inkişaf alətləri, paketlər və modullar daxildir. Tərtibatçılar Go dili ilə veb proqramlar, API-lər, verilənlər bazası sistemləri, şəbəkə proqram təminatı və sair kimi çoxlu müxtəlif növ proqramlar yarada bilərlər.

Go dilinin əsas xüsusiyyətlərindən biri sürətli tərtib prosesidir. Go C dilinə bənzər sintaksis strukturundan və C++ kimi dillərdə obyekt yönümlü xüsusiyyətlərdən istifadə edir. Zibil toplama, yüngül iplər, kapsullaşdırılmış tip sistemlər və dinamik yaddaşın idarə edilməsi kimi xüsusiyyətlər də Go dilində mövcuddur.

Go dilinin digər mühüm xüsusiyyəti onun effektiv paket idarəetmə sistemidir. Go dilində modullar və paketlər proqram tərtibatçılarına kodlarını nizamlı şəkildə təşkil etməyə və təkmilləşdirməyə imkan verir. Beləliklə, tərtibatçılar asanlıqla təkrar istifadə edilə bilən kodları yaza bilərlər.

Nəticədə, Go dilinin əsas xüsusiyyətlərinə sürətli tərtib prosesi, yüngül sintaksis strukturu, effektiv paket idarəetməsi, zibil toplama və kapsullaşdırılmış tip sistemlər kimi xüsusiyyətlər daxildir. Go-da inkişaf etdirmək bir çox proqram növləri üçün əlverişli seçimdir və proqram təminatının hazırlanması prosesini sürətləndirməyə kömək edə bilər.

**Məzmun:**

* [Values](#values)
* [Variables](#variables)
* [Constants](#constants)
* [If-Else](#if-else)
* [For](#for)
* [Switch](#switch)
* [Arrays](#arrays)
* [Slices](#slices)
* [Maps](#maps)
* [Range](#range)
* [Functions](#functions)
* [Variadic Functions](#variadic-functions)
* [Closures](#closures)
* [Recursion](#recursion)
* [Pointers](#pointers)
* [String and Runes](#strings-and-runes)
* [Structs](#structs)
* [Methods](#methods)
* [Interfaces](#interfaces)
* [Struct Embedding](#struct-embedding)
* [Errors](#errors)
* [Goroutines](#goroutines)
* [Channel](#channel)
* [Select](#select)
* [Timeouts](#timeouts)
* [Non-Blocking Channel](#non-blocking-channel)
* [Closing Channels](#closing-channels)
* [Range over Channels](#range-over-channels)
* [Timers](#timers)
* [Tickers](#ticker)
* [Worker Pools](#worker-pools)
* [Wait Groups](#waitgroups)
* [Rate Limiting](#rate-limiting)
* [Atomic Counters](#atomic-counters)
* [Sorting](#sorting)
* [Panic](#panic)
* [Defer](#defer)
* [Recover](#recover)
* [String Functions](#string-functions)
* [Text Templates](#text-templates)
* [JSON](#json)

***

## Values

Dəyərlər Go proqramlaşdırma dilində dəyişənlər tərəfindən daşınan məlumatlardır. Dəyərlər sabit və ya dəyişən ola bilər və müxtəlif məlumat növlərini təmsil edir.

Go dilində əsas məlumat növləri bunlardır:

* Ədədi məlumat növləri: Ədədi dəyərləri ifadə etmək üçün int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr, float32, float64, complex64, complex128, byte, rune kimi məlumat növlərindən istifadə olunur.
* Boolean məlumat növü: bool məlumat növü yalnız doğru və ya yanlış qiymətlər qəbul edə bilən məlumat növüdür.
* String data type: string data type mətn və ya simvol sətirinin qiymətlərini ifadə etmək üçün istifadə olunur.
* Mürəkkəb məlumat növləri: struct, array, slice, map, channel kimi məlumat tipləri çoxsaylı məlumat elementlərini bir yerdə saxlamaq və müəyyən bir məqsədə xidmət etmək üçün istifadə olunur.

Dəyərlərin növləri Go dilində statik olaraq müəyyən edilir. Dəyişənə qiymət təyin etdikdə bu dəyişənin tipi müəyyən edilir və biz bu tipi sonra dəyişə bilmərik.

```go
var age int
age = 32

age = "thirty-two" // compile error: cannot use "thirty-two" (type string) as type int in assignment
```

Bu nümunədə yaş dəyişəni ilk olaraq int kimi elan edilir və sonra dəyişən yaş üçün 32 rəqəmi təyin edilir. Sonra "thirty-two" sətri yaş dəyişəninə təyin edilməyə çalışılır və proqram xəta verir. Yaş dəyişəni int növü kimi təyin olunduğu üçün ona sətir tipli qiymət təyin edilə bilməz. Dəyərlər proqramları müxtəlif məqsədlər üçün istifadə etməyə imkan verir. Məsələn, rəqəmsal əməliyyatı yerinə yetirmək üçün int dəyərindən istifadə edə bilərik və ya mətn əməliyyatını yerinə yetirmək üçün string dəyərindən istifadə edə bilərik.

***

## Variables

Dəyişənlər Go proqramlaşdırma dilində məlumatların saxlanması üçün istifadə olunan əsaslardan biridir. Dəyişənə qiymət təyin etməklə biz proqramda həmin dəyişənin saxladığı qiymətdən istifadə edə bilərik.

Dəyişənlərin elan edilməsi var açar sözü ilə həyata keçirilir. Müəyyən ediləcək dəyişənin adı və məlumat tipi müəyyən edilir. Heç bir ilkin dəyər verilmədikdə, Go dilində dəyişənlər standart dəyərə malikdirlər.

```go
var name string
name = "John Doe"
```

Bu nümunədə ad adlı dəyişən müəyyən edilir və bu dəyişənin tipi sətir kimi müəyyən edilir. Sonra, ad dəyişəninə "John Doe" sətri təyin edilir. Dəyişənlərin dəyəri dəyişdirilə bilər və onlar müxtəlif məlumat tiplərində ola bilər.

```go
var age int
age = 32

age = "thirty-two" // compile error: cannot use "thirty-two" (type string) as type int in assignment
```

Bu misalda əvvəlcə yaş adlı dəyişən müəyyən edilir və bu dəyişənin növü int kimi müəyyən edilir. Sonra, dəyişən yaşa 32 nömrəsi verilir.

Daha sonra yaş dəyişəninə "thirty-two" sətri təyin edilməyə çalışılır və proqram xəta verir. Yaş dəyişəni int növü kimi təyin olunduğu üçün ona sətir tipi dəyəri təyin edilə bilməz.

Dəyişənlər proqramlarda müəyyən bir məqsədə xidmət etmək üçün istifadə olunur. Məsələn, istifadəçinin adı, yaşı və ya bir sıra ədədi dəyərlər saxlanıla bilər.

## Constants

Sabitlər (constants) Go proqramlaşdırma dilində proqramın heç bir yerində dəyişdirilə bilməyən sabit dəyərlərdir. Sabitlər bir dəfə müəyyən edilir və sonra proqramın istənilən yerində istifadə edilə bilər.

Sabitlər const açar sözü ilə müəyyən edilir və onların məlumat növləri göstərilir. Sabitlər işə salınmalıdır və sonra dəyişdirilə bilməz.

```go
const pi = 3.14159
const welcomeMessage = "Welcome to Go programming"
```

Bu nümunədə, sabit pi float64 növü kimi müəyyən edilir və 3.14159-a mənimsədilir. Sonra, welcomeMessage sabiti sətir kimi müəyyən edilir və "Welcome to Go programming" olaraq işə salınır.

Sabitlər müəyyən bir məqsəd üçün istifadə ediləcək proqramların sabit dəyərlərini saxlamaq üçün istifadə olunur. Məsələn, pi sayı və ya xüsusi mesaj və ya səhv kodu. Sabitlər proqramları daha oxunaqlı və davamlı etməyə kömək edir və proqramın müxtəlif yerlərində eyni sabitdən dəfələrlə istifadə etməyi asanlaşdırır.

Sabitlər proqramın istənilən yerində istifadə oluna bildiyi üçün onlar müxtəlif fayllar arasında da paylaşıla bilər. Bundan əlavə, sabitlərin dəyişdirilə bilməməsi proqram səhvlərini azaldır və təhlükəsizliyi artırır.

## if/else

if və else ifadələri, Go programlaşdırma dilində, müəyyən şərtlərin doğru və ya yanlış olduğu hallarda fərqli kod bloklarının işləməsini həyata keçirir.

```go
if x > 0 {
    fmt.Println("Positive number")
} else if x < 0 {
    fmt.Println("Negative number")
} else {
    fmt.Println("Zero")
}
```

Bu nümunədə, if ifadəsi, x dəyişkənin 0 dan böyük olması halında "Positive number" mətnini yazdıracaq. Əgər x dəyişəni 0 dan böyük deyilsə, else if ifadəsi yoxlanılacaq ve x dəyişəninin 0 dan kiçik olması halında "Negative number" mətnini yazdıracaq. Əgər x dəyişəni 0 dan böyük və ya kiçik deyilsə, else bloku işləyəcək ve "Zero" mətnini yazdıracaqdır.

```go
if və else ifadələri, qarışıq şərtləri idarə etmək üçün də istifadə oluna bilər.
if x > 10 && x < 20 {
    fmt.Println("x is between 10 and 20")
} else if x > 20 && x < 30 {
    fmt.Println("x is between 20 and 30")
} else {
    fmt.Println("x is not between 10 and 30")
}
```

Bu nümunədə, if ifadəsi, x dəyişənin 10 ve 20 arasında olması halında "x is between 10 and 20" mətnini yazdıracaqdır. Əgər x dəyişəni 10 ve 20 arasında deyilsə, else if ifadesi yoxlanılacaq və x dəyişənin 20 və 30 arasında olması halında "x is between 20 and 30" mətnini yazdıracaqdır. Əgər x dəyişəni 10 və 30 arasında deyilsə, else bloku işələyəcəkdir və "x is not between 10 and 30" mətnini yazdıracaqdır.

## For

for dövür operatırı, Go programlasdırma dilində, müəyyən bir şərt doğru olduğu halda təkrarlanan kod bloklarını ifadə etmək üçün istifadə olunur. for dövrü, bir başlanğıc halı, bir şərt və bir addım dəyərinə sahibdir.

```go
for i := 0; i < 5; i++ {
    fmt.Println(i)
}
```

Bu nümunədə, for dövrü, i dəyişəninin 0 dəyərindən başlayaraq, i dəyişəni < 5 şərtini ödədiyi müddətcə təkrarlanan bir dövrdür. Dövrdəki hər adımdda i dəyişəni 1 artırılır və nəticədə i dəyəri 4 olur.

for dövrü, şərt hissəsi doğru olana qədər təkrarlanır. Şərt hissəsi doğru olmadığında, dövr sonlanır. Ayrıca, for dövrü break və ya continue ifadələri ilə də idarə edilə bilir.

```go
for i := 0; i < 10; i++ {
    if i == 3 {
        break
    }
    if i%2 == 0 {
        continue
    }
    fmt.Println(i)
}
```

Bu nümunədə, for dövrü, i dəyişəninin 0 dəyərindən başlayaraq, i dəyişəni < 10 şərtini ödəyənədək təkrarlanan bir dövrdür. Dövrdəki hər adımdda, i dəyişəni 1 artırılır.

Dövr, if şərti ilə kəsilir ve break ifadesi işə düşür. Ayrıca, i dəyəri 3 olduğunda da dövr sonlanır.

Dövr içindəki digər if şərti isə, əgər i dəyəri cüt ədəddirsə bir sonrakı addıma keçərək i dəyərini yazdırmaz. Bu şərtin səbəbi, sadəcə tək ədədləri yazdırmaq istəməmizdir.

Sonsuz dövr yaratmaq üçün aşağıdakı nümunəyə baxaq.

```go
i:=0
for  {
    if i == 100 {
        break
    }
    fmt.Println(i)
	
	i++
}
```

Bu nümunədə i dəyişənimizə 0 dəyərini mənimsədirik. Daha sonra dövr daxilində i əgər 100-ə bərabər olarsa break ifadəsi ilə dövrü sonlandırırıq, əks halda dövr davam edəcəkdir

## Switch

Switch ifadəsi müəyyən şərtlər əsasında müxtəlif əməliyyatları yerinə yetirmək üçün Go proqramlaşdırma dilində istifadə olunur:

```go
day := "sunday"

switch day {
case "monday":
    fmt.Println("Today is Monday")
case "tuesday":
    fmt.Println("Today is Tuesday")
case "wednesday":
    fmt.Println("Today is Wednesday")
case "thursday":
    fmt.Println("Today is Thursday")
case "friday":
    fmt.Println("Today is Friday")
case "saturday":
    fmt.Println("Today is Saturday")
case "sunday":
    fmt.Println("Today is Sunday")
default:
    fmt.Println("Invalid day")
}
```

Bu misalda switch ifadəsi gün dəyişəninin dəyərindən asılı olaraq müxtəlif əməliyyatlar yerinə yetirir. Gün dəyişəni "monday"dirsə, "Today is Monday" mətnini çap edir. Gün dəyişəni "tuesday"dırsa, "Today is Tuesday" mətnini çap edir. Bu şəkildə, gün dəyişəninin dəyərindən asılı olaraq müxtəlif kod blokları icra edilə bilər.

default ifadəsi bütün şərtlər doğru olmadığı təqdirdə işləyəcək kod blokuna aiddir.

```go
switch x {
case 1:
    fmt.Println("x is 1")
case 2:
    fmt.Println("x is 2")
case 3:
    fmt.Println("x is 3")
default:
    fmt.Println("x is not 1, 2 or 3")
}
```

Bu misalda switch ifadəsi x dəyişəninin qiymətindən asılı olaraq müxtəlif əməliyyatlar yerinə yetirir. Əgər x dəyişəni 1-dirsə, o, "x-1-dir" mətnini çap edir. Əgər x dəyişəni 2-dirsə, o, "x-2-dir" mətnini çap edir. Əgər x dəyişəni 3-dürsə, o, “x-3-dür” mətnini çap edir. Əgər x dəyişəni 1, 2 və ya 3 deyilsə, standart blok işləyəcək və "x is not 1, 2 or 3" mətnini çap edəcək.

## Arrays

Array , Go proqramlaşdırma dilində eyni tipdə bir neçə dəyişəni tək bir dəyişkəndə saxlamağa imkan verən bir məlumat strukturudur. Bir array əvvəlcədən müəyyən edilmiş ölçüyə və müəyyən bir məlumat tipinə malikdir.

```go
var a [5]int
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
fmt.Println(a)
```

Bu nümunədə, a adlı bir array müəyyən edilir və ölçüsü 5 olaraq təyin edilir. Array-in elementlərinə a\[index] sintaksisi ilə daxil olmaq olar və elementlər tək-tək təyin edilə bilər. Bu nümunədə, array elementlərinə müvafiq olaraq 1, 2, 3, 4 və 5 dəyərləri təyin edilir və fmt.Println(a) ifadəsi istifadə edilərək bütün array-in elementləri ekrana çıxarılır.

Array-lərin ölçüsü bir dəfə təyin edildikdən sonra dəyişdirilə bilməz. Ancaq Go dilindəki dil xüsusiyyətləri ilə array ölçüləri dəyişdirilə bilər. Bu xüsusiyyət, Go dilində slice adlanan bir məlumat strukturu ilə həyata keçirilir.

```go
a := [5]int{1, 2, 3, 4, 5}
fmt.Println(a)
```

Bu nümunədə, a adlı bir array müəyyən edilir və elementləri {1, 2, 3, 4, 5} olaraq təyin edilir. Array-in ölçüsü elementlərin sayına bərabər olmalıdır. Bu nümunədə, a adlı array {1, 2, 3, 4, 5} dəyərləri ilə başlatılır və bütün elementləri ekrana çıxarılır.

## Slices

Dilimin əsas məlumat strukturlarından biri olan slices, Go proqramlaşdırma dilində ölçüsü dəyişdirilə bilən, elastik bir array məlumat strukturdur. Slices, array-lərin bir alt hissəsi kimi qəbul edilə bilər və Go dilində tez-tez istifadə olunur.

```go
a := []int{1, 2, 3, 4, 5}
fmt.Println(a)
```

Bu nümunədə, a adlı bir dilim təyin edilir və elementləri {1, 2, 3, 4, 5} olaraq təyin edilir. Array-lərdən fərqli olaraq, dilimlərin ölçüsü təyin edilmə mərhələsində göstərilmir. Slices boş da təyin edilə bilər.

```go
var a []int
a = append(a, 1)
a = append(a, 2, 3, 4, 5)
fmt.Println(a)
```

Bu nümunədə, a adlı bir dilim təyin edilir və append() funksiyası ilə elementlərinə müvafiq olaraq 1, 2, 3, 4 və 5 dəyərləri əlavə edilir. append() funksiyası, bir dilimə bir və ya bir neçə element əlavə etmək üçün istifadə olunur.

Dilimlər, array-lərdən fərqli olaraq, bir alt hissə kimi də təyin edilə bilər.

```go
a := []int{1, 2, 3, 4, 5}
b := a[1:4]
fmt.Println(b)
```

Bu nümunədə, a adlı bir dilim təyin edilir və elementlərinə {1, 2, 3, 4, 5} dəyərləri verilir. b adlı bir dilim isə a diliminin 1-ci indeksindən 4-cü indeksinə qədər olan alt hissəsini əhatə edir. b dilimindəki elementlər {2, 3, 4} olaraq göstərilir.

## Maps

Map, Go proqramlaşdırma dilində bir açar-dəyər cütləri kolleksiyasıdır. Map məlumat strukturu, digər proqramlaşdırma dillərindəki dictionary, hash table və ya associative array kimi məlumat strukturlarına bənzəyir. Bir Map məlumat strukturu, müəyyən bir açar üçün bir dəyər saxlayır.

```go
var colors map[string]string
colors = make(map[string]string)
colors["red"] = "#FF0000"
colors["green"] = "#00FF00"
colors["blue"] = "#0000FF"
fmt.Println(colors)
```

Bu nümunədə, colors adlı bir Map təyin edilir və make() funksiyası ilə yaradılır. colors məlumat strukturu red, green və blue açarlarına sahib rəng kodları ilə doldurulur və fmt.Println(colors) ifadəsi istifadə edilərək Map məlumat strukturundakı bütün açar-dəyər cütləri ekrana çıxarılır.

Map məlumat strukturu, digər proqramlaşdırma dillərindəki məlumat strukturlarından fərqli olaraq, açarlar və dəyərlər üçün müəyyən bir məlumat tipini göstərmək məcburiyyətində deyil. Açarlar və dəyərlər fərqli məlumat tiplərində ola bilər.

```go
ages := map[string]int{
    "Alice": 25,
    "Bob":   30,
    "Carol": 35,
}
fmt.Println(ages)
```

Bu nümunədə, ages adlı bir Map təyin edilir və string tipində açarlar və int tipində dəyərlərlə əlaqəli cütlər təyin olunur. Map məlumat strukturunun yaradılması və elementlərin əlavə edilməsi make() funksiyası ilə birləşdirilərək də həyata keçirilə bilər.

## Range

range ifadəsi, Go proqramlaşdırma dilində müəyyən bir array, dilim və ya map üzərində dövr yaratmaq üçün istifadə olunur. range ifadəsi, məlumat strukturundakı bütün elementləri tək-tək götürmək üçün istifadə olunur.

```go
numbers := []int{1, 2, 3, 4, 5}
for i, num := range numbers {
    fmt.Println("index:", i, "number:", num)
}
```

Bu nümunədə, numbers adlı bir dilim təyin edilir və elementlərinə {1, 2, 3, 4, 5} dəyərləri verilir. range ifadəsi istifadə edilərək, numbers dilimindəki bütün elementlər tək-tək götürülür və for dövrü içində istifadə olunur. i dəyişəni dövrün sırasındakı indeks dəyərini, num dəyişəni isə indeksdəki elementi təmsil edir. Bu nümunədə, indekslər və elementlər birgə yazdırılır.

```go
colors := map[string]string{
    "red":   "#FF0000",
    "green": "#00FF00",
    "blue":  "#0000FF",
}
for color, code := range colors {
    fmt.Println("color:", color, "code:", code)
}
```

Bu nümunədə, colors adlı bir map təyin edilir və red, green və blue açarlarına sahib rəng kodları verilir. range ifadəsi istifadə edilərək, colors xəritəsindəki bütün açar-dəyər cütləri tək-tək götürülür və for dövrü içində istifadə olunur. color dəyişəni açarı, code dəyişəni isə açara uyğun dəyəri təmsil edir. Bu nümunədə, rənglər və onların kodları birgə yazdırılır.

## Functions

Funksiya, Go proqramlaşdırma dilində, müəyyən bir vəzifəni yerinə yetirən kod bloklarını ifadə edir. Bir funksiya, bir və ya bir neçə parametr ala bilər, bir əməliyyatı həyata keçirə bilər və bir və ya bir neçə nəticə qaytara bilər.

```go
func add(a int, b int) int {
return a + b
}

result := add(5, 10)
fmt.Println(result)
```

Bu nümunədə, add adlı bir funksiya təyin edilir. Funksiya, a və b adlı iki int tipində parametr qəbul edir və bu parametrlər toplanaraq nəticə int tipində geri qaytarılır. add funksiyası, 5 və 10 parametrləri ilə çağırılır və nəticə fmt.Println(result) ifadəsi ilə yazdırılır.

```go
func swap(a, b string) (string, string) {
    return b, a
}

x, y := swap("hello", "world")
fmt.Println(x, y)
```

Bu nümunədə, swap adlı bir funksiya təyin edilir. Funksiya, a və b adlı iki string tipində parametr qəbul edir və bu parametrləri bir-biri ilə dəyişdirərək geri qaytarır. swap funksiyası, "hello" və "world" parametrləri ilə çağırılır və geri qaytarılan nəticələr x və y dəyişənləri tərəfindən qəbul edilir. Nəticələr fmt.Println(x, y) ifadəsi ilə yazdırılır.

## Variadic Functions

Variadic funksiyalar, Go proqramlaşdırma dilində dəyişən sayda arqument qəbul edən funksiyalardır. Bu funksiyalar bir və ya daha çox arqument qəbul edə bilər və arqumentlərin sayını dəyişən olaraq təyin etməyə imkan verir.

```go
func sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

fmt.Println(sum(1, 2, 3, 4, 5))
fmt.Println(sum(2, 4, 6))
```

Bu nümunədə, sum adlı bir funksiya təyin edilir. Funksiya, nums adlı int tipində dəyişən sayda parametr qəbul edir və onların cəmini qaytarır. Funksiyanın daxilində for dövrü istifadə edərək, nums dilimindəki bütün elementlərin cəmi hesablanır. Funksiya sum(1, 2, 3, 4, 5) və sum(2, 4, 6) şəklində çağırılır və nəticələr ekrana yazdırılır.

```go
func concatenate(sep string, strs ...string) string {
    result := ""
    for i, str := range strs {
        if i > 0 {
            result += sep
        }
        result += str
    }
    return result
}

fmt.Println(concatenate(", ", "foo", "bar", "baz"))
fmt.Println(concatenate("-", "hello", "world"))
```

Bu nümunədə, concatenate adlı bir funksiya təyin edilir. Funksiya, sep adlı string tipində bir parametr və strs adlı dəyişən sayda string tipində parametrlər qəbul edir. Funksiyanın daxilində for dövrü istifadə edilərək, strs dilimindəki bütün elementlər birləşdirilir və sep ayracı ilə birgə yazılır. Funksiya concatenate(", ", "foo", "bar", "baz") və concatenate("-", "hello", "world") şəklində çağırılır və nəticələr ekrana yazdırılır.

## Closures

Closures, Go proqramlaşdırma dilində, bir funksiyanın başqa bir funksiyanın daxilində yaradılması ilə əmələ gələn bir strukturdur. Bu struktur, bir funksiyanın daxilində olan başqa bir funksiyaya istinad edərək, funksiyanın işlədiyi mühitin xaricindəki dəyişənlərə giriş imkanı verir.

```go
func outer() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

increment := outer()
fmt.Println(increment())
fmt.Println(increment())
fmt.Println(increment())
```

Bu örnekte, outer adlı bir fonksiyon tanımlanır. Fonksiyon, bir iç fonksiyon döndürür ve iç fonksiyon, count adlı bir değişkene erişim sağlar. increment adlı bir değişkene outer() fonksiyonu atanır ve bu değişken ile iç fonksiyon çalıştırılır. count değişkeni, increment() çağrıldıkça artar ve her seferinde artışı ekrana yazdırılır.

```go
func adder(a int) func(int) int {
    return func(b int) int {
        return a + b
    }
}

addFive := adder(5)
fmt.Println(addFive(2))
fmt.Println(addFive(3))
```

Bu nümunədə, adder adlı bir funksiya təyin edilir. Funksiya, a adlı bir int tipində parametr qəbul edir və bir daxili funksiya qaytarır. Daxili funksiya, b adlı bir int tipində parametr qəbul edir və a ilə b parametrlərinin cəmini geri qaytarır. addFive adlı bir dəyişənə adder(5) funksiyası təyin edilir və bu dəyişən ilə daxili funksiya işlədilir. addFive(2) və addFive(3) çağırıldıqca nəticələr ekrana yazdırılır.

## Recursion

Recursion, Go proqramlaşdırma dilində, bir funksiyanın özünü çağırmasıdır. Bu struktur, müəyyən bir şərt yerinə yetirilənə qədər funksiyanın təkrarlanaraq işləməsini təmin edir.

```go
func factorial(n int) int {
    if n == 0 {
        return 1
    }
    return n * factorial(n-1)
}

fmt.Println(factorial(5))
```

Bu nümunədə, factorial adlı bir funksiya təyin edilir. Funksiya, n adlı bir int tipində parametr qəbul edir və faktorialı hesablayır. Funksiya daxilində, if şərti istifadə edilərək n dəyərinin 0 olub-olmadığı yoxlanılır. Əgər n 0-dırsa, 1 qaytarılır. Əgər n 0 deyil, başqa bir dəyərdirsə, funksiya özünü yenidən çağıraraq faktorialı hesablayır. factorial(5) çağırıldıqda nəticə ekrana yazdırılır.

```go
func fibonacci(n int) int {
    if n < 2 {
        return n
    }
    return fibonacci(n-1) + fibonacci(n-2)
}

fmt.Println(fibonacci(10))
```

Bu nümunədə, fibonacci adlı bir funksiya təyin edilir. Funksiya, n adlı bir int tipində parametr qəbul edir və Fibonacci sayını hesablayır. Funksiya daxilində, if şərti istifadə edilərək n dəyərinin 2-dən kiçik olub-olmadığı yoxlanılır. Əgər n 2-dən kiçikdirsə, n dəyəri geri qaytarılır. Əks halda, funksiya özünü yenidən çağıraraq Fibonacci sayını hesablayır. fibonacci(10) çağırıldıqda nəticələr ekrana yazdırılır.

## Pointers

Pointers, Go proqramlaşdırma dilində, bir dəyişənin yaddaş ünvanını saxlayan bir məlumat tipidir. Yaddaş ünvanı, dəyişənin yaddaşda yerləşdiyi yerdir.

```go
func zeroVal(val int) {
    val = 0
}

func zeroPtr(ptr *int) {
    *ptr = 0
}

x := 5
zeroVal(x)
fmt.Println(x)

y := 5
zeroPtr(&y)
fmt.Println(y)
```

Bu nümunədə, zeroVal adlı bir funksiya təyin edilir. Funksiya int tipində bir parametr qəbul edir və val dəyişəninin dəyərini 0 olaraq dəyişir, amma bu dəyişiklik orijinal dəyişənə təsir etmir. Digər tərəfdən, zeroPtr adlı bir funksiya isə bir pointer (\*int) qəbul edir və bu göstərici ilə göstərilən dəyişənin dəyərini 0 olaraq dəyişdirir. x dəyişəninə 5 dəyəri atanır və zeroVal(x) çağırılır, amma x dəyişməz. y dəyişəninə isə 5 dəyəri atanır və zeroPtr(\&y) çağırıldıqda, y 0 olur.

```go
func swap(a, b *int) {
    temp := *a
    *a = *b
    *b = temp
}

x := 5
y := 10
swap(&x, &y)
fmt.Println(x, y)
```

Bu nümunədə, swap adlı funksiya iki pointer (\*int) qəbul edir və həmin dəyişənlərin dəyərlərini bir-biri ilə dəyişir. x-ə 5 və y-ə 10 dəyəri verilir. swap(\&x, \&y) çağırıldıqdan sonra, x 10 olur, y isə 5 olur.

## Strings and Runes

Strings, Go proqramlaşdırma dilində, Unicode simvollarının birləşdirilməsi ilə yaradılan bir simvol sətiridir. Hər bir simvol 1-4 byte arasında dəyişən ölçülərdə ola bilər.

```go
str := "hello"
fmt.Println(str)
fmt.Println(str[0])
fmt.Println(str[1:3])
```

Bu nümunədə, str adlı bir string dəyişəni təyin edilir və "hello" dəyəri verilir. str dəyişəni ekrana çap olunur, sonra isə str\[0] ifadəsi istifadə edilərək sətirin ilk simvolu olan "h" ekrana çap edilir. str\[1:3] ifadəsi istifadə edilərək sətirin ikinci və üçüncü simvolları olan "el" ekrana çap edilir.

```go
for i, r := range "hello" {
    fmt.Printf("%d: %s\n", i, string(r))
}
```

Bu nümunədə, for döngüsü istifadə edilərək range funksiyası ilə "hello" stringindəki hər bir simvola giriş edilir. Hər simvolun mövqeyi və dəyəri ekrana çap olunur. Runes, Go proqramlaşdırma dilində, bir Unicode simvolunun birləşməsini ifadə edən bir məlumat tipidir. Runes, 1-4 bayt arasında dəyişən ölçülərdə ola bilən simvolları təmsil etmək üçün istifadə olunur.

```go
str := "こんにちは"
for i, r := range str {
    fmt.Printf("%d: %c\n", i, r)
}
```

Bu nümunədə, str adlı bir string dəyişəni təyin edilir və "こんにちは" dəyəri verilir. for döngüsü istifadə edilərək range funksiyası ilə hər bir simvola giriş edilir və simvolun mövqeyi və dəyəri ekrana çap olunur.

## Structs

Structlar, Go proqramlaşdırma dilində, fərqli məlumat tiplərini özündə birləşdirən bir məlumat quruluşudur. Bu quruluşda, müxtəlif məlumat tiplərinə sahib məlumatları bir arada saxlaya və bu məlumatlar üzərində əməliyyatlar apara bilərsiniz.

```go
type Person struct {
    Name string
    Age  int
}

var p Person
p.Name = "John Doe"
p.Age = 42
fmt.Println(p)
fmt.Printf("Name: %s, Age: %d\n", p.Name, p.Age)
```

Bu nümunədə, Person adlı bir struct yaradılır və Name adlı bir string tipində və Age adlı bir int tipində iki xüsusiyyət təyin edilir. var açar sözü ilə p adlı bir Person tipində dəyişən təyin edilir. p dəyişəninin Name və Age xüsusiyyətləri, p.Name və p.Age ifadələri istifadə edilərək təyin edilir və fmt.Println() funksiyası ilə p dəyişəni ekrana çap edilir. Əlavə olaraq, p.Name və p.Age ifadələri ilə, Name və Age xüsusiyyətləri ayrıca ekrana çap edilir.

```go
type Rectangle struct {
    width  float64
    height float64
}

func (r Rectangle) area() float64 {
    return r.width * r.height
}

r := Rectangle{width: 3.0, height: 4.0}
fmt.Println(r.area())
```

Bu nümunədə, Rectangle adlı bir struct yaradılır və width adlı bir float64 tipində və height adlı bir float64 tipində iki xüsusiyyət təyin edilir. area adlı bir funksiya, Rectangle tipində bir parametr qəbul edir və düzbucaqlının sahəsini hesablayır. Funksiyanın qaytardığı nəticə düzbucaqlının sahəsidir. r adlı bir Rectangle dəyişəni yaradılır və width və height xüsusiyyətlərinə 3.0 və 4.0 dəyərləri verilir. r.area() ifadəsi istifadə edilərək, düzbucaqlının sahəsi hesablanır və nəticə ekrana çap edilir.

## Methods

Metodlar, Go proqramlaşdırma dilində, bir məlumat quruluşuna məxsus əməliyyatları yerinə yetirmək üçün istifadə edilən funksiya növüdür. Bu əməliyyatlar, məlumat quruluşunun xüsusiyyətləri üzərində işləyərək nəticə yaradır.

```go
type Rectangle struct {
    width  float64
    height float64
}

func (r Rectangle) area() float64 {
    return r.width * r.height
}

func (r Rectangle) perimeter() float64 {
    return 2 * (r.width + r.height)
}

r := Rectangle{width: 3.0, height: 4.0}
fmt.Println(r.area())
fmt.Println(r.perimeter())
```

Bu nümunədə, Rectangle adlı bir struct yaradılır və width adlı bir float64 tipində və height adlı bir float64 tipində iki xüsusiyyət təyin olunur. area adlı funksiya, Rectangle tipində parametr qəbul edir və düzbucaqlının sahəsini hesablayır. Funksiyanın qaytardığı nəticə, düzbucaqlının sahəsidir. perimeter adlı funksiya da Rectangle tipində parametr alır və düzbucaqlının perimetrini hesablayır. Funksiyanın qaytardığı nəticə, düzbucaqlının perimetridir. r adlı bir Rectangle dəyişəni yaradılır və width və height xüsusiyyətlərinə 3.0 və 4.0 dəyərləri təyin olunur. r.area() və r.perimeter() ifadələrindən istifadə edərək düzbucaqlının sahəsi və perimetri hesablanır və nəticələr ekrana yazdırılır.

```go
type Person struct {
    Name string
    Age  int
}

func (p *Person) setName(name string) {
    p.Name = name
}

func (p *Person) setAge(age int) {
    p.Age = age
}

func (p Person) getName() string {
    return p.Name
}

func (p Person) getAge() int {
    return p.Age
}

p := Person{Name: "John Doe", Age: 42}
p.setName("Jane Doe")
p.setAge(35)
fmt.Printf("Name: %s, Age: %d\n", p.getName(), p.getAge())
```

Bu nümunədə, Person adlı bir struct yaradılır və Name adlı bir string tipində və Age adlı bir int tipində iki xüsusiyyət təyin olunur. setName adlı funksiya, Person tipində pointer (\*Person) parametr qəbul edir və şəxsin adını dəyişir. setAge adlı funksiya, Person tipində pointer (\*Person) parametr qəbul edir və şəxsin yaşını dəyişir. getName adlı funksiya, Person tipində parametr qəbul edir və şəxsin adını qaytarır. getAge adlı funksiya isə şəxsin yaşını qaytarır. p adlı bir Person dəyişəni yaradılır və Name və Age xüsusiyyətlərinə "John Doe" və 42 dəyərləri təyin olunur. Daha sonra, p.setName("Jane Doe") və p.setAge(35) ifadələrindən istifadə edərək şəxsin adı və yaşı dəyişdirilir.

## Interfaces

Go dilində interfeys, bir və ya bir neçə metodun müəyyən imza dəstini təyin edən bir məlumat tipidir. Bu imza dəsti, bir məlumat tipinin hansı metodları tətbiq etməli olduğunu göstərir. Bu səbəbdən, interfeyslər, bir məlumat tipinin hansı xüsusiyyətlərə malik olduğunu təyin etmək üçün istifadə olunur.

Məsələn, bir fiqurun sahəsini hesablamaq üçün area adlı bir funksiya təyin etdiyinizi düşünün. Kvadrat, dairə və düzbucaqlı kimi müxtəlif fiqurlar area funksiyasını fərqli şəkildə tətbiq edirlər. Bu halda, hər bir fiqurun sahəsini hesablamaq üçün ayrı-ayrı funksiyalar təyin etmək əvəzinə, interfeys istifadə edərək hamısını eyni tipdə bir məlumatda toplaya bilərsiniz.

```go
package main

import (
	"fmt"
	"math"
)

type Shape interface {
	area() float64
}

type Circle struct {
	radius float64
}

type Rectangle struct {
	width, height float64
}

type Square struct {
	side float64
}

func (c Circle) area() float64 {
	return math.Pi * c.radius * c.radius
}

func (r Rectangle) area() float64 {
	return r.width * r.height
}

func (s Square) area() float64 {
	return s.side * s.side
}

func getArea(shape Shape) float64 {
	return shape.area()
}

func main() {
	circle := Circle{radius: 5}
	rectangle := Rectangle{width: 5, height: 10}
	square := Square{side: 5}

	fmt.Printf("Circle area: %f\n", getArea(circle))
	fmt.Printf("Rectangle area: %f\n", getArea(rectangle))
	fmt.Printf("Square area: %f\n", getArea(square))
}
```

Bu nümunədə, Shape adlı bir interfeys təyin olunur və area adlı bir funksiya imzası göstərilir. Circle, Rectangle və Square adlı üç fərqli struktura malik məlumat tipləri təyin olunur və hər biri area adlı funksiyanı həyata keçirir. getArea adlı bir funksiya təyin olunur və parametri Shape tipindədir. Bu funksiya, sahəsi hesablanacaq fiqur məlumatını alır və area funksiyasını çağıraraq fiqurun sahəsini hesablamağa imkan verir. main funksiyasında isə, nümunə olaraq circle, rectangle və square adlı üç fərqli dəyişən yaradılır. Bu dəyişənlərin hər biri getArea funksiyasına parametr olaraq verilir və hər bir fiqurun sahəsi hesablanaraq ekrana yazdırılır.

**Output**:

```go
Circle area: 78.539816
Rectangle area: 50.000000
Square area: 25.000000
```

## Struct Embedding

Go dilində struct embedding, bir struct-ın başqa bir struct daxilində yerləşdirilməsi ilə digər struct-ın sahələrinə və metodlarına birbaşa giriş imkanı verir. Bu, təkcə kod təkrarını azaltmır, həm də kompozisiyanı təşviq edir, beləliklə, bir struct-ın başqa bir struct-a miras buraxmadan onun funksionallığını təmin etməyə imkan verir.

```go
package main

import "fmt"

type Person struct {
	Name string
	Age  int
}

func (p Person) SayHello() {
	fmt.Printf("Hello, my name is %s and I'm %d years old\n", p.Name, p.Age)
}

type Employee struct {
	Person
	Company string
}

func main() {
	emp := Employee{
		Person: Person{
			Name: "John",
			Age:  30,
		},
		Company: "ABC Inc.",
	}

	emp.SayHello()
}
```

Bu nümunədə, Person adlı bir struct təyin edilir və Name və Age adlı iki sahəsi var. Eyni zamanda SayHello adlı bir funksiyası mövcuddur.

Employee adlı bir struct təyin edilir və onun içərisində Person struct-ı yerləşdirilir. Employee-nin əlavə olaraq Company adlı bir sahəsi də var. main funksiyasında, emp adlı bir Employee dəyişəni yaradılır. Bu dəyişənin Person sahəsinə Person tipində bir dəyər atanır. SayHello funksiyası emp dəyişəni üzərindən çağırılır və nəticə ekrana yazdırılır.

**Output:**

```go
Hello, my name is John and I'm 30 years old
```

Bu nümunədə, Employee adlı struktur, Person strukturunu daxili olaraq yerləşdirir. Bu, Employee strukturuna Person-un bütün sahələrinə və funksiyalarına birbaşa çıxış imkanı verir. Beləliklə, Employee strukturunu istifadə edərək həm Employee, həm də Person məlumatlarına asanlıqla giriş əldə etmək mümkündür. Bu xüsusiyyət, kod təkrarını azaldır və strukturları daha modul halına gətirir.

## Errors

Go dilində səhv (error) idarəetməsi, error adlı bir məlumat tipi vasitəsilə həyata keçirilir. Bu tip, bir funksiyanın nəticəsi olaraq ya bir səhv mesajı, ya da nil dəyərini qaytarmaq üçün istifadə olunur. Bu, proqramın gözlənilməyən vəziyyətlərdə necə davranmalı olduğunu idarə etməyə kömək edir.

```go
package main

import (
	"errors"
	"fmt"
)

func divide(x, y float64) (float64, error) {
	if y == 0 {
		return 0, errors.New("Cannot divide by zero")
	}
	return x / y, nil
}

func main() {
	result, err := divide(10, 2)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(result)
	}

	result, err = divide(10, 0)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(result)
	}
}
```

Bu nümunədə, divide adlı bir funksiya təyin edilir və iki ədədi bölür. Lakin, əgər ikinci ədəd 0 olarsa, bir səhv qaytarır.

main funksiyasında, divide funksiyası iki fərqli parametr ilə çağırılır. err adlı bir səhv dəyişəni istifadə edilərək hər bir çağırışın nəticəsi yoxlanılır. Əgər səhv varsa, səhv mesajı ekrana yazdırılır. Əks halda, nəticə ekrana yazdırılır.

**Output**:

```go
5 <nil>
Cannot divide by zero
```

Bu nümunədə divide funksiyası sıfıra bölmə vəziyyətini idarə etmək üçün səhv idarəetməsindən istifadə edir. Əgər y sıfır olarsa, errors.New ilə yaradılmış səhv qaytarılır. Proqram err dəyişəni ilə səhv olub-olmadığını yoxlayır və əgər səhv varsa, onu ekrana yazdırır. Bu yanaşma Go dilində səhv idarəetməsini daha etibarlı və idarə olunan edir.

## Goroutines

Go dilində goroutine-lər, eyni anda çalışan əməliyyatlardır. Goroutine-lər go açar sözü istifadə edilərək yaradılır və fərqli əməliyyatları eyni vaxtda həyata keçirmək üçün istifadə olunur.

```go
package main

import (
	"fmt"
	"time"
)

func sayHello() {
	fmt.Println("Hello")
}

func main() {
	go sayHello() // goroutine
	time.Sleep(time.Second)
	fmt.Println("World")
}
```

Bu nümunədə, sayHello adlı bir funksiya təyin edilir və "Hello" mesajını ekrana yazdırır.

main funksiyasında, sayHello funksiyası bir goroutine olaraq çağırılır. Bu səbəbdən, sayHello funksiyasının icrası digər əməliyyatlardan müstəqil olaraq baş verir. time.Sleep funksiyası bir saniyəlik gözləmə müddəti əlavə edir. Nəticədə, "World" mesajı ekrana yazdırılır.

**Output**:

```go
Hello
World
```

Bu nümunədə, goroutine istifadə edərək sayHello funksiyası eyni anda çalışdırıldı. sayHello funksiyası goroutine olaraq çağırıldığı üçün digər əməliyyatlardan müstəqil işləd və nəticədə ekrana "Hello" mesajı yazdırıldıktan sonra "World" mesajı yazdırıldı.

## Channel

Go dilində kanal (channel), goroutine-lər arasında məlumat ötürmək üçün istifadə olunan bir məlumat strukturudur. Kanal, make açar sözü ilə yaradılır və <- operatoru ilə məlumat göndərmə və qəbul etmə əməliyyatları həyata keçirilir.

```go
package main

import "fmt"

func main() {
	messages := make(chan string)

	go func() { messages <- "Hello" }()

	msg := <-messages
	fmt.Println(msg)
}
```

Bu nümunədə, messages adlı bir kanal yaradılır.

go açar sözü ilə bir goroutine yaradılır və bu goroutine messages kanalına "Hello" mesajını göndərir.

main funksiyasında, msg adlı bir dəyişkənə messages kanalından bir mesaj alınır və ekrana yazdırılır.

**Output**:

```go
Hello
```

Bu örnekte, channel kullanarak, bir goroutine'dan ana iş parçacığına bir mesaj gönderildi. Bu nedenle, goroutine işlemi tamamlandıktan sonra ana iş parçacığı channel'dan mesajı alır ve sonucu ekrana yazdırır.

Kanallar, Golang'de birçok durumda kullanılabilir, örneğin:

Bu nümunədə, kanal istifadə edərək bir goroutine-dən ana iş parçacığına bir mesaj göndərildi. Bu səbəbdən, goroutine tamamlandıqdan sonra ana iş parçacığı kanaldan mesajı alır və nəticəni ekrana yazdırır.

Kanallar Go dilində bir çox vəziyyətdə istifadə oluna bilər, məsələn:

1. Goroutine-lər arasında məlumat mübadiləsi üçün
2. Sinxronizasiya əməliyyatları üçün
3. Tətbiqinizin performansını artırmaq üçün (paralel emal etmək)
4. Goroutine-lər arasında məlumat yarışlarını qarşısını almaq üçün
5. Tapşırıqların koordinasiyası və sinxronizasiyası üçün

## Select

Go dilində, select açar sözü bir neçə kanalı `(channel)` dinləyərək hansı kanalın mesaj göndərdiyini müəyyənləşdirə bilər. Bu xüsusiyyət kanalların sinxronizasiyasını asanlaşdırır və fərqli goroutin-lər arasında mesajlaşmanı idarə edir.

```go
package main

import (
	"fmt"
	"time"
)

func main() {
	c1 := make(chan string)
	c2 := make(chan string)

	go func() {
		time.Sleep(time.Second * 1)
		c1 <- "one"
	}()

	go func() {
		time.Sleep(time.Second * 2)
		c2 <- "two"
	}()

	for i := 0; i < 2; i++ {
		select {
		case msg1 := <-c1:
			fmt.Println("received", msg1)
		case msg2 := <-c2:
			fmt.Println("received", msg2)
		}
	}
}
```

Bu nümunədə, `c1` və `c2` adlı iki kanal yaradılır və mesaj göndərmə prosesi üçün goroutin-lər yaradılır.

main funksiyasında, select açar sözü istifadə edilərək, c1 və c2 kanalları dinlənilir. İlk olaraq, goroutin-lər arasındakı gözləmə müddətinə görə, `c1` kanalından bir mesaj alınır və ekrana yazdırılır. Daha sonra, `c2` kanalından bir mesaj alınır və ekrana yazdırılır.

**Output:**

```golang
received one
received two
```

Bu nümunədə, select açar sözü istifadə edilərək, `c1` və `c2` kanallarını dinləyən bir for döngüsü yaradıldı. Bu, mesaj alım müddətinə əsaslanaraq fərqli kanalların dinlənilməsinə imkan verir. Nəticədə, goroutin-lər arasındakı mesajlaşma müəyyən bir qaydada həyata keçirilir və select açar sözü istifadə edilərək sinxronizasiya təmin edilir.

## Timeouts

Go dilində, timeout əməliyyatları, xüsusilə şəbəkə əməliyyatları zamanı vacibdir. `time` paketindən istifadə edərək, müəyyən bir müddət gözləmək mümkündür. Əgər müddət aşılırsa, timeout xətası baş verir.

```go
package main

import (
	"fmt"
	"time"
)

func main() {
	c1 := make(chan string, 1)

	go func() {
		time.Sleep(time.Second * 2)
		c1 <- "result 1"
	}()

	select {
	case res := <-c1:
		fmt.Println(res)
	case <-time.After(time.Second * 1):
		fmt.Println("timeout 1")
	}

	c2 := make(chan string, 1)

	go func() {
		time.Sleep(time.Second * 2)
		c2 <- "result 2"
	}()

	select {
	case res := <-c2:
		fmt.Println(res)
	case <-time.After(time.Second * 3):
		fmt.Println("timeout 2")
	}
}
```

Bu nümunədə `c1` və `c2` adlı iki kanal yaradılır və mesaj göndərmə əməliyyatları üçün goroutine-lər istifadə olunur. İlk olaraq, `select` açar sözü istifadə edərək, `c1` kanalından bir mesaj gözlənilir. Amma `time.After` ilə bir saniyə vaxt limiti təyin edilir. Əgər bu vaxt aşılırsa, "timeout 1" mesajı çap olunur.

Sonra, eyni əməliyyat `c2` kanalı üçün edilir və bu dəfə 3 saniyə gözlənilir.

**Output**:

```go
timeout 1
result 2
```

## Non-Blocking Channel Operations

Go dilində, kanallar adətən bloklama xüsusiyyətinə malikdir. Yəni, bir goroutine bir kanala mesaj göndərmək və ya mesaj almaq istəyirsə, həmin əməliyyat tamamlanana qədər dayanar. Lakin, `select` açar sözü istifadə edilərək, non-blocking əməliyyatlar da həyata keçirmək mümkündür.

```go
package main

import "fmt"

func main() {
	messages := make(chan string)
	signals := make(chan bool)

	select {
	case msg := <-messages:
		fmt.Println("received message", msg)
	default:
		fmt.Println("no message received")
	}

	msg := "hello"
	select {
	case messages <- msg:
		fmt.Println("sent message", msg)
	default:
		fmt.Println("no message sent")
	}

	select {
	case msg := <-messages:
		fmt.Println("received message", msg)
	default:
		fmt.Println("no message received")
	}

	select {
	case sig := <-signals:
		fmt.Println("received signal", sig)
	default:
		fmt.Println("no signal received")
	}

	signal := true
	select {
	case signals <- signal:
		fmt.Println("sent signal", signal)
	default:
		fmt.Println("no signal sent")
	}

	select {
	case sig := <-signals:
		fmt.Println("received signal", sig)
	default:
		fmt.Println("no signal received")
	}
}
```

Bu nümunədə, `messages` adlı bir kanal yaradılır və göndərilməmiş mesajın yoxlanılması üçün `select` istifadə edilir. Ardından, bir mesaj yaradılır və kanala göndərilməyə çalışılır. Əgər kanal boşdursa, "no message sent" çap olunur.

**Output**:

```go
no message received
sent message hello
no message received
no signal received
sent signal true
received signal true
```

## Closing Channels

Go dilində, kanalların bağlanması, mesajların göndərilməsi və alınması arasında sinxronizasiya yaradır. `close` funksiyası ilə bir kanal bağlana bilər. Bağlanan kanala daha artıq mesaj göndərilə bilməz və bu kanaldan mesajlar alınmaz.

```go
package main

import "fmt"

func main() {
	jobs := make(chan int, 5)
	done := make(chan bool)

	go func() {
		for {
			j, more := <-jobs
			if more {
				fmt.Println("received job", j)
			} else {
				fmt.Println("received all jobs")
				done <- true
				return
			}
		}
	}()

	for j := 1; j <= 3; j++ {
		jobs <- j
		fmt.Println("sent job", j)
	}

	close(jobs)
	fmt.Println("sent all jobs")

	<-done
}
```

Bu nümunədə `jobs` adlı bir kanal yaradılır və goroutine bu kanaldan mesajlar qəbul edir. `close` funksiyası ilə kanal bağlanır və bütün işlərin tamamlandığı siqnalı `done` kanalına göndərilir.

**Output**:

```go
sent job 1
sent job 2
sent job 3
sent all jobs
received job 1
received job 2
received job 3
received all jobs
```

## Range over Channels

Go-da `range` açar sözü istifadə edərək kanaldan mesajlar almaq mümkündür. Bu zaman kanal açıq qaldığı müddətdə mesajlar qəbul edilir. Kanal bağlandıqda isə `range` döngüsü dayanır.

```go
package main

import "fmt"

func main() {
	queue := make(chan string, 2)
	queue <- "one"
	queue <- "two"
	close(queue)

	for elem := range queue {
		fmt.Println(elem)
	}
}
```

Bu nümunədə, `queue` adlı bir kanal yaradılır və iki mesaj kanala yerləşdirilir. Kanal bağlandıqdan sonra, `range` döngüsü ilə kanalın bütün elementləri çap edilir.

**Output**:

```
one
two
```

## Timers

Go dilində `time` paketindən istifadə edərək timerlər yaratmaq mümkündür. Timerlər müəyyən bir müddətdən sonra bir siqnal göndərir. `time.NewTimer` funksiyası müəyyən edilmiş vaxtdan sonra bir siqnal göndərən bir timer yaradır.

```go
package main

import (
	"fmt"
	"time"
)

func main() {
	timer1 := time.NewTimer(time.Second * 2)

	<-timer1.C
	fmt.Println("Timer 1 fired")

	timer2 := time.NewTimer(time.Second)
	go func() {
		<-timer2.C
		fmt.Println("Timer 2 fired")
	}()

	stop2 := timer2.Stop()
	if stop2 {
		fmt.Println("Timer 2 stopped")
	}

	time.Sleep(time.Second * 2)
}
```

Bu nümunədə, iki timer yaradılır. Birincisi 2 saniyə sonra işə düşür və "Timer 1 fired" çap edir. İkincisi isə 1 saniyə sonra işə düşmədən dayandırılır, buna görə də "Timer 2 stopped" çap olunur.

**Output**:

```go
Timer 1 fired
Timer 2 stopped
```

## Tickers

Go-da `Ticker` periodik olaraq müəyyən bir intervalla siqnal göndərən bir mexanizmdir. `time.NewTicker` funksiyası ilə ticker yaradılır və hər dəfə təyin edilmiş intervalla siqnallar alır.

```go
package main

import (
	"fmt"
	"time"
)

func main() {
	ticker := time.NewTicker(time.Millisecond * 500)
	done := make(chan bool)

	go func() {
		for {
			select {
			case <-done:
				return
			case t := <-ticker.C:
				fmt.Println("Tick at", t)
			}
		}
	}()

	time.Sleep(time.Millisecond * 1600)
	ticker.Stop()
	done <- true
	fmt.Println("Ticker stopped")
}
```

Bu nümunədə `ticker` hər 500 millisekundda bir "tick" siqnalı göndərir və bu siqnallar çap edilir. `time.Sleep` ilə müəyyən müddətdən sonra ticker dayandırılır və proqram sonlanır.

**Output**:

```go
Tick at 2023-09-10 12:34:56.123456789 +0000 UTC m=+0.500123456
Tick at 2023-09-10 12:34:56.623456789 +0000 UTC m=+1.000123456
Tick at 2023-09-10 12:34:57.123456789 +0000 UTC m=+1.500123456
Ticker stopped
```

## Worker Pools

Worker pool-ları Go-da paralel hesablama üçün istifadə olunur. Bir neçə goroutine bir-birindən müstəqil işləri eyni zamanda yerinə yetirə bilər. Bu şəkildə bir neçə işçinin bir pool-da işləməsi təşkil edilə bilər.

```go
package main

import (
	"fmt"
	"time"
)

func worker(id int, jobs <-chan int, results chan<- int) {
	for j := range jobs {
		fmt.Println("worker", id, "started  job", j)
		time.Sleep(time.Second)
		fmt.Println("worker", id, "finished job", j)
		results <- j * 2
	}
}

func main() {
	const numJobs = 5
	jobs := make(chan int, numJobs)
	results := make(chan int, numJobs)

	for w := 1; w <= 3; w++ {
		go worker(w, jobs, results)
	}

	for j := 1; j <= numJobs; j++ {
		jobs <- j
	}
	close(jobs)

	for a := 1; a <= numJobs; a++ {
		<-results
	}
}
```

Bu nümunədə 3 işçi yaradılır və 5 iş onları icra etməsi üçün göndərilir. Hər işçinin hansı işi icra etməyə başladığı və tamamladığı çap olunur.

**Output**:

```go
worker 1 started  job 1
worker 2 started  job 2
worker 3 started  job 3
worker 1 finished job 1
worker 1 started  job 4
worker 2 finished job 2
worker 2 started  job 5
worker 3 finished job 3
worker 1 finished job 4
worker 2 finished job 5
```

## WaitGroups

WaitGroup quruluşu Go dilində goroutinlər arasında sinxronizasiya təmin etmək üçün istifadə edilən bir mexanizmdir. WaitGroup quruluşu Go-nun `sync` paketində yerləşir.

WaitGroup proqramçılara işləyəcək funksiyaların sayını əvvəlcədən müəyyən etməyə və həmin funksiyaların tamamlanmasını gözləməyə imkan verir. Hər goroutine işləmə bitdikdə, WaitGroup quruluşundakı `Done()` funksiyasını çağırır. WaitGroup-dakı `Wait()` funksiyası isə bütün funksiyaların tamamlanmasını gözləyir.

Aşağıdakı nümunə, WaitGroup quruluşunun istifadəsini göstərir:

```go
package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()

    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

    wg.Wait()

    fmt.Println("All workers done")
}
```

Bu nümunədə, 5 işçi funksiyası `worker()` funksiyası çağırılır. WaitGroup quruluşu, hər işçi funksiyası başlamazdan əvvəl `Add()` funksiyası ilə gözlənilən işlərin sayını artırır. Hər işçi funksiyası tamamlandıqda, `Done()` funksiyası ilə bir işin başa çatdığı bildirilir. `Wait()` funksiyası isə bütün işlərin bitməsini gözləmək üçün istifadə olunur. Nəticədə, bütün işçi funksiyaları bitdikdən sonra `main()` funksiyası "All workers done" mesajını çap edir.

**Ouput**:

```go

Worker 5 starting
Worker 3 starting
Worker 4 starting
Worker 1 starting
Worker 2 starting
Worker 5 done
Worker 3 done
Worker 2 done
Worker 1 done
Worker 4 done
All workers done
```

## Rate Limiting

Rate Limiting, bir API və ya xidmətin istifadəçinin müəyyən bir zaman ərzində edə biləcəyi sorğu sayını məhdudlaşdırmaq üçün istifadə edilən bir metoddur. Bu metod, tətbiqin və ya xidmətin həddindən artıq yüklənməsinin qarşısını almaq üçün istifadə oluna bilər.

Go dilində, Rate Limiting etmək üçün `time` paketi istifadə oluna bilər. Bu paket, müəyyən bir zaman çərçivəsində müəyyən bir əməliyyatın yerinə yetirilməsi üçün nə qədər gözləmək lazım olduğunu hesablamaq üçün istifadə edilir.

Aşağıdakı nümunədə, `time.Ticker` və `time.Sleep` istifadə edilərək Rate Limiting nümunəsi göstərilir:

```go
package main

import (
    "fmt"
    "time"
)

func main() {
    requests := make(chan int, 5)

    for i := 1; i <= 5; i++ {
        requests <- i
    }
    close(requests)

    limiter := time.Tick(200 * time.Millisecond)

    for req := range requests {
        <-limiter
        fmt.Println("request", req, time.Now())
    }
}
```

Bu nümunədə, `requests` adlı bir kanal yaradılır və içinə 5 sorğu əlavə edilir. Daha sonra `limiter` adlı bir `time.Ticker` yaradılır və 200 millisaniyəlik bir müddətlə məhdudlaşdırılır.

Daha sonra, `requests` kanalındakı hər bir sorğu məhdudlaşdırıcıya uyğun olaraq işlədir. Hər sorğu arasında 200 millisaniyəlik fasilə ilə işlənməsi təmin edilir.

**Output:**

```go
request 1 2023-05-23 15:56:01.46705 +0300 +03 m=+0.201167209
request 2 2023-05-23 15:56:01.667008 +0300 +03 m=+0.401132584
request 3 2023-05-23 15:56:01.867014 +0300 +03 m=+0.601146918
request 4 2023-05-23 15:56:02.067052 +0300 +03 m=+0.801193043
request 5 2023-05-23 15:56:02.267025 +0300 +03 m=+1.001173793
```

Bu şəkildə, Rate Limiting istifadə edərək sorğular müəyyən bir sürətlə işlənir.

## Atomic Counters

Atomic Counters mövzusu, Go dilində paralelliyi idarə etmək üçün istifadə olunan bir mövzudur. Çoxsaylı Go proqramları bir neçə goroutine tərəfindən paylaşılan verilərlə işlədiyinə görə, bu verilərin eyni anda bir neçə goroutine tərəfindən dəyişdirilməsi labüddür. Bu dəyişikliklər düzgün idarə edilmədikdə, proqram gözlənilməz şəkildə işləyə bilər.

Go dilində Atomic Counter-lər `sync/atomic` paketində mövcuddur. Bu paketdə `AddInt64`, `CompareAndSwapInt64`, `SwapInt64`, `LoadInt64`, və `StoreInt64` kimi metodlar mövcuddur. Bu metodlar, bir neçə goroutine tərəfindən eyni anda daxil olunan dəyişkənin təhlükəsiz şəkildə dəyişdirilməsini təmin edir.

Aşağıdakı nümunə, Atomic Counter istifadə edərək, 10 goroutine tərəfindən paylaşılan bir sayaç dəyişkəninin təhlükəsiz şəkildə artırılmasını göstərir:

```go
package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

func main() {
    var counter int64 = 0
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            atomic.AddInt64(&counter, 1)
        }()
    }
    wg.Wait()
    fmt.Println("Counter:", counter)
}
```

Bu nümunədə, `counter` adlı dəyişkən `int64` tipində bir Atomic Counter olaraq təyin olunur və başlanğıcda 0 dəyəri ilə müəyyən edilir. Daha sonra, 10 goroutine yaradılır və hər biri `atomic.AddInt64` metodundan istifadə edərək `counter` dəyişkənini artırır. Bu metod `&counter` ünvanını alır və `counter` dəyişkəninə atomik şəkildə 1 əlavə edir.

Nəticədə, Atomic Counter veri quruluşu, çoxsaylı goroutine-lər tərəfindən paylaşılan dəyişkənlərin təhlükəsiz şəkildə dəyişdirilməsini təmin edir və paralellik idarəsində mühüm rol oynayır.

## Sorting

Golang-də sıralama əməliyyatı üçün `sort` paketi istifadə olunur. Bu paket daxilində müxtəlif məlumat tiplərinə aid sıralama əməliyyatları aparmaq üçün funksiyalar mövcuddur.

Ən çox istifadə olunan iki sıralama funksiyası `sort.Ints()` və `sort.Strings()` funksiyalarıdır. `sort.Ints()` funksiyası `int` tipində slice-i kiçikdən böyüyə doğru sıralayır. `sort.Strings()` funksiyası isə `string` tipində slice-i əlifba sırası ilə sıralayır.

Bundan əlavə, `sort.Float64s()` funksiyası da mövcuddur və `float64` tipində slice-i kiçikdən böyüyə doğru sıralayır.

Bundan başqa, `sort.Slice()` funksiyası ilə xüsusi sıralama əməliyyatları da həyata keçirilə bilər. Bu üsul, bir slice və bir `Less` funksiyası alır. `Less` funksiyası iki slice elementini müqayisə etmək üçün istifadə olunur və nəticəsinə əsasən elementlərin sıralanmasına qərar verilir. Bu funksiya ilə slice elementlərinə görə xüsusi sıralama əməliyyatları həyata keçirilə bilər.

Məsələn, aşağıdakı nümunədə `sort.Slice()` funksiyası istifadə edilərək xüsusi bir sıralama əməliyyatı həyata keçirilir:

```go
package main

import (
	"fmt"
	"sort"
)

type Person struct {
	Name string
	Age  int
}

func main() {
	people := []Person{
		{"Alice", 25},
		{"Bob", 30},
		{"Charlie", 20},
	}

	sort.Slice(people, func(i, j int) bool {
		return people[i].Age < people[j].Age
	})

	fmt.Println(people)
}
```

Bu nümunədə, `Person` tipində bir slice təyin edilir və `sort.Slice()` funksiyası istifadə edilərək `Age` sahəsinə görə kiçikdən böyüyə doğru sıralama aparılır.

**Output**:

```go
[{Charlie 20} {Alice 25} {Bob 30}]
```

İlk nümunədə, `sort.Ints()` funksiyası istifadə edilərək `ints` adlı bir slice kiçikdən böyüyə doğru sıralanır və nəticə ekrana yazdırılır:

```go
package main

import (
	"fmt"
	"sort"
)

func main() {
	ints := []int{5, 2, 7, 1, 9, 3, 8, 6, 4}
	sort.Ints(ints)
	fmt.Println(ints)

	if sort.IntsAreSorted(ints) {
		fmt.Println("Slice is sorted.")
	} else {
		fmt.Println("Slice is not sorted.")
	}
}
```

**Output**:

```go
[1 2 3 4 5 6 7 8 9]
Slice is sorted.
```

İkinci nümunədə isə `sort.Strings()` funksiyası istifadə edilərək `strings` adlı bir slice əlifba sırasına görə sıralanır və nəticə ekrana yazdırılır:

```go
package main

import (
	"fmt"
	"sort"
)

func main() {
	strings := []string{"apple", "banana", "cherry", "date", "elderberry", "fig"}
	sort.Strings(strings)
	fmt.Println(strings)

	if sort.StringsAreSorted(strings) {
		fmt.Println("Slice is sorted.")
	} else {
		fmt.Println("Slice is not sorted.")
	}
}
```

**Output**:

```go
[apple banana cherry date elderberry fig]
Slice is sorted.
```

## Panic

`panic`, Go dilində bir proqram işləmə zamanı gözlənilməz bir vəziyyətlə qarşılaşdıqda, proqramın icrasını dayandıraraq bir xəta mesajı göstərməsini təmin edən xüsusiyyətdir.

`panic` ciddi səhvləri idarə etmək üçün istifadə edilməlidir və mümkün qədər qarşısını almağa çalışılmalıdır. Lakin, kritik bir xəta baş verdikdə, proqramın dərhal dayandırılması lazım olan vəziyyətlərdə `panic` faydalı ola bilər.

**`panic` istifadə nümunəsi:**

```go
package main

import "fmt"

func main() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("Panic yakalandı:", err)
		}
	}()

	divideByZero()
	fmt.Println("Bu kod çalışacak mı?")
}

func divideByZero() {
	numerator := 10
	denominator := 0
	result := numerator / denominator
	fmt.Println("Sonuç:", result)
}
```

Bu nümunədə, `divideByZero` adlı bir funksiya var. Bu funksiya daxilində `numerator` 10 və `denominator` 0 olaraq təyin edilir, daha sonra `result` dəyişəninə `numerator`u `denominator`ə bölürük. Lakin, `denominator`un 0 olması vəziyyətində, sıfıra bölmə xətası baş verir və bu, `panic` vəziyyətini yaradır.

**Açıqlama:**

1. **Panic və recover istifadəçi:**\
   `defer` istifadə edərək bir funksiyanı gecikdiririk, burada `recover()` funksiyası `panic` vəziyyətlərini ələ keçirir. Əgər `panic` vəziyyəti yaranarsa, `recover()` həmin `panic` səbəbini tapır və işləyir.
2. **Sıfıra bölmə xətası:**\
   `divideByZero()` funksiyasında sıfıra bölmə cəhdi olur, bu isə `panic` vəziyyətinə səbəb olur. Lakin, `recover()` bu `panic`i ələ keçirir və proqramın dayanmadan davam etməsinə imkan yaradır.
3. **Nəticə:**\
   Proqram bu vəziyyəti tutaraq "`Panic yakalandı: runtime error: integer divide by zero`" kimi bir mesaj göstərir və proqram sonrakı kodu icra etməyə davam edir.

Bu yanaşma proqramların gözlənilməz xətaları idarə etməsinə və stabil qalmasına kömək edir.

## Defer

`defer`, Go dilindəki xüsusi bir açar sözdür və müəyyən bir funksiyanın sonunda icra ediləcək funksiyaları və ya ifadələri təyin etmək üçün istifadə olunur. Defer ifadələri, funksiyanın sonunda nə olursa olsun, yəni funksiyanın hər hansı bir səbəbdən sona çatması halında belə icra ediləcəkdir.

Defer ifadələrini istifadə edərək, bir funksiyanın sonunda açılmış faylları, bağlanmamış verilənlər bazası bağlantılarını, şəbəkə bağlantılarını və s. tez və təhlükəsiz bir şəkildə bağlamaq mümkündür.

Nümunə olaraq, bir fayl açılır və `defer` ilə funksiyanın sonunda fayl bağlanılır:

```go
package main

import (
	"fmt"
	"os"
)

func main() {
	
	f, err := os.Open("example.txt")
	if err != nil {
		fmt.Println(err)
		return
	}
	
	defer f.Close()

	// faylı oxuma
	b := make([]byte, 1024)
	n, err := f.Read(b)
	if err != nil {
		fmt.Println(err)
		return
	}
	
	fmt.Println(string(b[:n]))
}
```

Bu nümunədə, `os.Open` funksiyası istifadə edilərək "example.txt" faylı açılır. `defer` ifadəsi istifadə edilərək fayl bağlama əməliyyatı funksiyanın sonunda icra ediləcək şəkildə planlaşdırılır. Daha sonra, `Read` funksiyası istifadə edilərək faylın məzmunu oxunur və ekrana çap edilir.

Burada diqqət edilməli mühüm bir məqam, `defer` ifadəsinin ən sona yazılmamasıdır. `defer` ifadəsi, bağlanacaq olan faylı açan ifadə ilə eyni blokda olmalıdır. Əks halda, `defer` ifadəsi funksiyanın sonuna qədər gözləməyə davam edəcəkdir.

Aşağıda `Recover` mövzusunda kod və şərhlərin azərbaycanca tərcüməsi, çıxış hissəsi isə ingiliscədir:

## Recover

`recover` funksiyası, bir Go proqramının işləmə müddətində meydana gələn panic-ləri idarə etmək üçün istifadə olunur. Əgər bir panic baş verərsə, `recover` funksiyası panic-in baş verdiyi funksiyanın içində çağırılarsa, panic səbəbindən dayandırılmış əməliyyatın idarəsini geri alır və proqramın normal şəkildə davam etməsini təmin edir.

`recover` funksiyası adətən `defer` ifadələri ilə birlikdə istifadə olunur. Bu sayədə, mümkün bir panic vəziyyətində proqramın idarəsi `defer` ifadələrindəki əməliyyatlardan ən sonuncusu olan `recover` funksiyasına ötürülür və beləliklə proqramın davam etməsi təmin edilir.

Nümunə olaraq, bir panic baş verdikdə proqramın işləməsini dayandırmaq əvəzinə, `recover` funksiyası vasitəsilə proqramın normal şəkildə davam etməsi təmin edilə bilər:

```go
package main

import "fmt"

func main() {
    fmt.Println("Program starting...")

    // Defer ifadəsi istifadə edərək panic vəziyyətlərində işlədiləcək funksiyanı müəyyən edirik.
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Panic occurred: This is a panic!:", r)
        }
    }()

    fmt.Println("Program continues...")

    // Burada bilərəkdən panic yaradırıq.
    panic("This is a panic")
}
```

Bu nümunədə, `panic` ifadəsi istifadə edilərək bilərəkdən bir panic yaradılır. `defer` ifadəsi istifadə edərək panic vəziyyətlərində işlədiləcək bir funksiya müəyyən edilir. `recover` funksiyası, funksiyanın içində çağırıldığı zaman, panic vəziyyətindən geri dönən dəyəri alır və `if` strukturu istifadə edilərək bu dəyər ilə işləyir.

**Output**

```go
Program starting...
Program continues...
Panic occurred: This is a panic!
This is a panic
```

## String Functions

Golang, string məlumatları üzərində əməliyyatlar aparmaq üçün bir çox hazır funksiyalar təqdim edir. Bu funksiyalar, string məlumatlarını parçalamaq, birləşdirmək, müqayisə etmək, axtarmaq, dəyişdirmək və daha bir çox əməliyyat aparmaq üçün istifadə edilə bilər.

Bəzi çox istifadə olunan string funksiyaları bunlardır:

* `len(s string) int`: Verilmiş string'in uzunluğunu qaytarır.
* `s[i] byte`: Verilmiş string'dəki göstərilən indeks nömrəsindəki simvolu qaytarır.
* `s + t`: İki string'i birləşdirir.
* `strings.Split(s, sep string) []string`: Verilmiş string'i, göstərilən ayırıcı simvollara görə parçalayır və bir array olaraq return edir.
* `strings.Join(a []string, sep string) string`: Verilmiş string arrayini, göstərilən ayırıcı simvol ilə birləşdirir və tək bir string olaraq return edir.
* `strings.Contains(s, substr string) bool`: Verilmiş string daxilində, göstərilən alt string'in olub olmadığını yoxlayır.
* `strings.HasPrefix(s, prefix string) bool`: Verilmiş string'in, göstərilən prefix ilə başlayıb başlamadığını yoxlayır.
* `strings.HasSuffix(s, suffix string) bool`: Verilmiş string'in, göstərilən suffix ilə bitib-bitmədiyini yoxlayır.
* `strings.Replace(s, old, new string, n int) string`: Verilmiş string'də, göstərilən köhnə string'i, göstərilən yeni string ilə dəyişdirir. İstəyə bağlı olaraq, göstərilən sayda dəyişdirmə edir.

Nümunə olaraq, bir string daxilində müəyyən bir xarakter dizisinin neçə dəfə keçdiyini tapmaq üçün `strings.Count` funksiyası istifadə edilə bilər:

```go
package main

import (
    "fmt"
    s "strings"
)

var p = fmt.Println

func main() {

    p("Contains:  ", s.Contains("test", "es"))
    p("Count:     ", s.Count("test", "t"))
    p("HasPrefix: ", s.HasPrefix("test", "te"))
    p("HasSuffix: ", s.HasSuffix("test", "st"))
    p("Index:     ", s.Index("test", "e"))
    p("Join:      ", s.Join([]string{"a", "b"}, "-"))
    p("Repeat:    ", s.Repeat("a", 5))
    p("Replace:   ", s.Replace("foo", "o", "0", -1))
    p("Replace:   ", s.Replace("foo", "o", "0", 1))
    p("Split:     ", s.Split("a-b-c-d-e", "-"))
    p("ToLower:   ", s.ToLower("TEST"))
    p("ToUpper:   ", s.ToUpper("test"))
}
```

**Output**:

```go
Contains:   true
Count:      2
HasPrefix:  true
HasSuffix:  true
Index:      1
Join:       a-b
Repeat:     aaaaa
Replace:    f00
Replace:    f0o
Split:      [a b c d e]
ToLower:    test
ToUpper:    TEST
```

## Text Templates

Template (şablon) faylları, Go dilində mətn əsaslı məlumatları formatlamaq üçün istifadə olunur. Bir şablon faylı, bir mətn sənədindəki məlumatların bir qismini yer tutucular və ya ifadələrlə dəyişdirərək yaradılır. Bu əməliyyat, bir şablon faylını bir məlumat strukturu ilə birləşdirərək həyata keçirilir.

Məsələn, bir e-poçt göndərimi zamanı, bir şablon faylı istifadə edərək mesajın məzmununu müəyyən bir formatda yaratmaq olar. Şablon faylında, dəyişənlər və funksiyalar istifadə edərək yer tutucular və ifadələr təyin edilə bilər. Daha sonra, bu dəyişənlər və funksiyalar real məlumat strukturu ilə birləşdirilərək nəticədə tamamlanmış mətn sənədi yaradılır.

Go dilində, şablon faylları `text/template` və `html/template` paketləri ilə yaradılır. Bu paketlər, şablon fayllarının yaradılmasını və işlənməsini təmin edən bir çox funksiya təqdim edir.

```go
package main

import (
    "fmt"
    "os"
    "text/template"
)

type Person struct {
    Name    string
    Age     int
    Country string
}

func main() {
    person := Person{
        Name:    "John",
        Age:     30,
        Country: "USA",
    }

    tpl := "My name is {{.Name}} and I am {{.Age}} years old. I live in {{.Country}}.\n"
    t := template.Must(template.New("person").Parse(tpl))

    err := t.Execute(os.Stdout, person)
    if err != nil {
        fmt.Println("Error executing template:", err)
    }
}
```

Bu nümunədə, `Person` adlı bir məlumat strukturu təyin edilmişdir. Sonra, `tpl` dəyişənində, şablonun bir mətn parçası mövcuddur. Şablon içərisində istifadə olunacaq dəyişənlər `{{.}}` işarələri arasına yazılır. Bu dəyişənlərə məlumat strukturundakı sahələr təyin ediləcəkdir.

Daha sonra, `template.Must()` funksiyası istifadə edilərək yeni bir `Template` obyekti yaradılır və şablon mətnini `Parse()` funksiyası ilə bu obyektə təyin edilir. Sonra, `Execute()` funksiyası istifadə edilərək, `Template` obyekti, məlumat strukturu və `os.Stdout` (standart çıxış) printeri istifadə edilərək işlənir və nəticə ekrana çıxarılır.

**Output**:

```go
My name is John and I am 30 years old. I live in USA.
```

## JSON

JSON (JavaScript Object Notation), yüngül bir veri mübadiləsi formatıdır və insanlar tərəfindən oxunması və yazılması asandır. Go dilində, `encoding/json` paketi JSON məlumatları ilə işləmək üçün istifadə edilə bilər.

JSON məlumatları, açar/dəyər cütləri və ya siyahılardan ibarət ola bilər. Açarlar bir string, dəyərlər isə JSON məlumatı ola bilər. JSON məlumatları `{}` (qıvrım mötərizələr) ilə göstərilən obyektlər şəklində də ola bilər.

Misal JSON məlumatı belə görünə bilər:

```json
{
    "name": "John Doe",
    "age": 30,
    "city": "New York"
}
```

Bu məlumat, `name`, `age` və `city` adlı üç açar saxlayır. `name` açarı bir string dəyəri, `age` açarı bir sayısal dəyər və `city` açarı da bir string dəyəridir.

Go dilində, JSON məlumatları üçün `struct` quruluşu istifadə edilə bilər. `struct` içərisindəki sahələr JSON məlumatındakı açarlarla uyğunlaşdırıla bilər. Daha sonra, `encoding/json` paketində olan `Marshal()` və `Unmarshal()` funksiyaları istifadə edilərək, Go obyekti və JSON məlumatı arasında dönüşüm edilə bilər.

Misal üçün, bir `Person` struct'ını tanıdaq və bu struct-ı JSON məlumatı ilə uyğunlaşdıraq:

```go
type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    City string `json:"city"`
}
```

Burada, `Person` struct-ının sahələri JSON məlumatındakı açarlarla uyğunlaşdırılmışdır. `Name` sahəsi üçün `name`, `Age` sahəsi üçün `age`, `City` sahəsi üçün isə `city` açarları istifadə olunmuşdur.

Bu struct-ı JSON məlumatına çevirmək üçün, `json.Marshal()` funksiyası istifadə edilə bilər:

```go
person := Person{Name: "John Doe", Age: 30, City: "New York"}
jsonData, err := json.Marshal(person)
if err != nil {
    fmt.Println("Error encoding JSON:", err)
}
fmt.Println(string(jsonData)) // {"name":"John Doe","age":30,"city":"New York"}
```

Burada, `Person` struct-ı bir `person` dəyişəni olaraq təyin edilir və `json.Marshal()` funksiyası istifadə edilərək `person` obyekti JSON məlumatına çevrilir. Əldə edilən JSON məlumatı `string()` funksiyası istifadə edilərək string tipinə çevrilir və ekrana çap olunur.

Başqa bir misal isə, JSON məlumatlarını Go məlumat strukturlarına çevirmək və əksinə etmək üçün `json.Marshal()` və `json.Unmarshal()` funksiyalarını istifadə etməkdir. Məsələn:

```go
package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    p1 := Person{"Alice", 30}
    jsonStr, err := json.Marshal(p1)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(jsonStr))

  
    var p2 Person
    jsonStr = []byte(`{"Name":"Bob","Age":40}`)
    err = json.Unmarshal(jsonStr, &p2)
    if err != nil {
        panic(err)
    }
    fmt.Println(p2)
}
```

Bu nümunədə, `Person` adlı bir struktur təyin edilir və `json.Marshal()` və `json.Unmarshal()` funksiyaları istifadə edilərək bu struktur JSON məlumatı olaraq kodlanır və təhlil edilir.

**Output**

```go
{"Name":"Alice","Age":30}
{Bob 40}
```


# Go Compiler Installations

Go proqramlaşdırma dilini istifadə etməyə başlamaq üçün Go compiler (dərleyicisi) quraşdırmaq lazımdır. Aşağıda Go compiler-in müxtəlif əməliyyat sistemlərində necə quraşdırılacağına dair addım-addım təlimatları təqdim edirəm.

## 1. Go Compiler-in Yüklənməsi

**1.1. Windows**

1. **Go-nun Rəsmi Veb Saytına Gedin**:
   * [Go-nun rəsmi yükləmə səhifəsinə](https://golang.org/dl/) daxil olun.
2. **Yükləmə Faylını Seçin**:
   * Windows üçün uyğun `.msi` faylını seçin və yükləyin (64-bit və ya 32-bit versiya).
3. **Quraşdırmanı Başladın**:
   * Yüklədiyiniz `.msi` faylını açın və quraşdırma prosesini izləyin.
4. **Sistem Dəyişənlərini Konfiqurasiya Edin**:
   * Quraşdırma başa çatdıqdan sonra, `GOPATH` və `GOROOT` mühit dəyişənlərini konfiqurasiya etməyə ehtiyac ola bilər. Adətən quraşdırma avtomatik olaraq bu dəyişənləri əlavə edir, amma əl ilə əlavə etmək lazım ola bilər.
5. **Təsdiqləmə**:
   * Komanda xəttinə (`cmd`) daxil olun və aşağıdakı əmri işlədərək quraşdırmanı təsdiqləyin:

     ```sh
     go version
     ```

**1.2. macOS**

1. **Go-nun Rəsmi Veb Saytına Gedin**:
   * [Go-nun rəsmi yükləmə səhifəsinə](https://golang.org/dl/) daxil olun.
2. **Yükləmə Faylını Seçin**:
   * macOS üçün uyğun `.pkg` faylını seçin və yükləyin.
3. **Quraşdırmanı Başladın**:
   * Yüklədiyiniz `.pkg` faylını açın və quraşdırma prosesini izləyin.
4. **Sistem Dəyişənlərini Konfiqurasiya Edin**:
   * Quraşdırma başa çatdıqdan sonra, `GOPATH` və `GOROOT` mühit dəyişənlərini `~/.bash_profile` və ya `~/.zshrc` faylınıza əlavə edə bilərsiniz:

     ```sh
     export GOROOT=/usr/local/go
     export GOPATH=$HOME/go
     export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
     ```
5. **Təsdiqləmə**:
   * Terminala daxil olun və aşağıdakı əmri işlədərək quraşdırmanı təsdiqləyin:

     ```sh
     go version
     ```

**1.3. Linux**

1. **Go-nun Rəsmi Veb Saytına Gedin**:
   * [Go-nun rəsmi yükləmə səhifəsinə](https://golang.org/dl/) daxil olun.
2. **Yükləmə Faylını Seçin**:
   * Linux üçün uyğun `.tar.gz` faylını seçin və yükləyin.
3. **Quraşdırma Faylını Açın**:
   * Terminala daxil olun və yüklədiyiniz `.tar.gz` faylını açın:

     ```sh
     tar -C /usr/local -xzf go1.x.x.linux-amd64.tar.gz
     ```
4. **Sistem Dəyişənlərini Konfiqurasiya Edin**:
   * `~/.bashrc` və ya `~/.zshrc` faylınıza aşağıdakı sətirləri əlavə edin:

     ```sh
     export GOROOT=/usr/local/go
     export GOPATH=$HOME/go
     export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
     ```
5. **Təsdiqləmə**:
   * Terminala daxil olun və aşağıdakı əmri işlədərək quraşdırmanı təsdiqləyin:

     ```sh
     go version
     ```

## 2. Quraşdırma Təsdiqlənməsi

Quraşdırma tamamlandıqdan sonra, `go version` əmrini işlədərək Go compiler-in düzgün quraşdırıldığını və versiyasını yoxlayın. Bu əmr Go-nun versiyasını göstərməlidir, məsələn:

```sh
go version
```

Çıxış:

```
go version go1.22.5 linux/amd64
```

Bu təlimatlarla Go compiler-i sisteminizə uğurla quraşdıra bilərsiniz.


# First Go Application

Go proqramlaşdırma dilində yeni bir layihə başlatmaq, müəyyən addımları izləməklə həyata keçirilir. Aşağıda Go-da yeni bir layihənin yaradılması üçün əsas addımlar və təlimatlar təqdim olunur.

## 1. Go-nun Quraşdırılmasını Təsdiqləmə

Əvvəlcə Go-nun sisteminizdə düzgün quraşdırıldığını təsdiqləməlisiniz. Terminala aşağıdakı əmri yazaraq Go-nun versiyasını yoxlayın:

```sh
go version
```

Çıxışda Go-nun versiyası göstərilməlidir, məsələn:

```
go version go1.20.2 linux/amd64
```

## 2. Proyekt Qovluğunun Yaradılması

Yeni bir Go layihəsi yaratmaq üçün ilk olaraq layihə qovluğunu yaradın:

```sh
mkdir my-go-project
cd my-go-project
```

Bu əmr yeni bir qovluq yaradacaq və həmin qovluğa daxil edəcəkdir.

## 3. Go Modulu İlə Proyekti İdarə Etmək

Go modulları layihənin asılılıqlarını idarə etmək üçün istifadə olunur. Proyektinizin kök qovluğunda `go.mod` faylı yaradaraq layihəni bir Go modulu kimi başlatmaq üçün aşağıdakı əmri işlədin:

```sh
go mod init my-go-project
```

Bu əmr `go.mod` faylını yaradacaq və layihənizin adını `my-go-project` olaraq təyin edəcək.

## 4. Go Kodunu Yazmaq

Layihə qovluğunda əsas Go kodunu yazmaq üçün bir `main.go` faylı yaradın. Məsələn:

```sh
touch main.go
```

`main.go` faylını bir mətn redaktoru ilə açın və aşağıdakı kodu əlavə edin:

```go
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
```

Bu sadə proqram ekrana "Salam, Dünya!" mesajını çap edəcək.

## 5. Proyekti İcra Etmək

Layihənizi icra etmək üçün terminalda aşağıdakı əmri yazın:

```sh
go run main.go
```

Çıxışda proqramın çap etdiyi mesajı görməlisiniz:

```
Hello, World!!
```

## 6. Proyekti Build Etmək

Proyekti binary fayl olaraq build etmək üçün aşağıdakı əmri istifadə edin:

```sh
go build
```

Bu əmr, `main.go` faylını oxuyacaq və `my-go-project` adında bir icra edilə bilən fayl yaradacaq. Binary faylı işə salmaq üçün aşağıdakı əmri yazın:

```sh
./my-go-project
```

Bu əmr eyni "SHello, World!" mesajını çap edəcək.

## 7. Proyektin Asılılıqlarını İdarə Etmək

Əgər layihənizdə xarici asılılıqlardan istifadə edirsinizsə, `go get` əmrini istifadə edərək bu asılılıqları əlavə edə bilərsiniz. Məsələn, bir kitabxana əlavə etmək üçün:

```sh
go get github.com/some/library
```

Bu əmr, `go.mod` faylınıza lazımlı asılılığı əlavə edəcək və `go.sum` faylını müvafiq şəkildə yeniləyəcək.

## 8. Proyekti Test Etmək

Layihənizdə testlər yazmışsınızsa, testləri icra etmək üçün aşağıdakı əmrdən istifadə edin:

```sh
go test
```

Bu əmr layihə qovluğunda olan bütün test fayllarını icra edəcək və nəticələri göstərəcək.

## 9. Proyekti Formatlamaq

Go kodunun standart formatlaşdırılmasını təmin etmək üçün `go fmt` əmrindən istifadə edin:

```sh
go fmt ./...
```

Bu əmr layihə qovluğundakı bütün Go fayllarını standart formatda formatlayacaq.

## Nəticə

Bu təlimatlarla siz Go-da yeni bir layihə yaratmağı, kod yazmağı, build etməyi və idarə etməyi öyrəndiniz. Go-nun modul idarəetməsi, test yazma və kod formatlama kimi xüsusiyyətlərindən istifadə edərək layihənizi effektiv bir şəkildə inkişaf etdirə bilərsiniz.


# Build on Different Platforms

Go proqramlaşdırma dili, çox platformlu tətbiqlər yaratmaq üçün geniş imkanlar təqdim edir. Bir Go proqramını tək bir əməliyyat sistemi üçün deyil, müxtəlif əməliyyat sistemləri və arxitekturalar üçün də tərtib etmək mümkündür. Bu xüsusiyyət Go-nun daxilində olan "cross-compilation" (çarpaz tərtib) dəstəyi sayəsində həyata keçirilir.

## Əsas Əmrlər və Müddəalar

Go-da çarpaz tərtibat üçün `GOOS` (operation system) və `GOARCH` (architecture) adlı iki mühüm mühit dəyişəni var. Bu dəyişənlər istifadə olunaraq, bir tətbiq istənilən əməliyyat sistemi və arxitektura üçün tərtib edilə bilər.

### `GOOS` Dəyərləri:

* **windows**: Windows əməliyyat sistemi
* **linux**: Linux əməliyyat sistemi
* **darwin**: macOS (Apple) əməliyyat sistemi
* **freebsd**: FreeBSD əməliyyat sistemi

### `GOARCH` Dəyərləri:

* **amd64**: 64-bit x86 prosessorlar üçün
* **386**: 32-bit x86 prosessorlar üçün
* **arm**: ARM arxitekturası üçün
* **arm64**: 64-bit ARM arxitekturası üçün

## Tərtibat Prosesi

Fərqli platformalar üçün Go tətbiqi qurmaq olduqca sadədir. Siz yalnız uyğun mühit dəyişənlərini təyin etməklə tətbiqinizi tərtib edə bilərsiniz. Aşağıda bəzi nümunələrə nəzər salaq.

## Linux üçün Windows-da Tərtibat:

Bir Windows maşınında Linux üçün Go tətbiqi qurmaq üçün aşağıdakı əmrdən istifadə edə bilərsiniz:

```bash
GOOS=linux GOARCH=amd64 go build -o myapp-linux
```

## macOS üçün Tərtibat:

Linux və ya Windows sistemində macOS üçün Go tətbiqi qurmaq üçün:

```bash
GOOS=darwin GOARCH=amd64 go build -o myapp-macos
```

## Windows üçün Tərtibat:

Linux və ya macOS sistemində Windows üçün Go tətbiqi qurmaq üçün:

```bash
GOOS=windows GOARCH=amd64 go build -o myapp-windows.exe
```

## ARM Arxitekturası üçün Tərtibat:

Əgər ARM cihazları üçün tərtib etmək istəyirsinizsə, məsələn, Raspberry Pi üçün:

```bash
GOOS=linux GOARCH=arm go build -o myapp-arm
```

## Faydaları

* **Daha sürətli inkişaf**: Bir platformda işləyərək bir çox fərqli platformalar üçün tətbiq tərtib etmək mümkündür.
* **Daha az konfiqurasiya**: Əlavə alət və ya mühitə ehtiyac olmadan tək bir maşından çoxlu platforma üçün tətbiqlər tərtib etmək mümkündür.
* **Sadə tərtibat prosesi**: Yalnız `GOOS` və `GOARCH` dəyişənlərini təyin etməklə fərqli platformalar üçün tərtib həyata keçirmək mümkündür.

Bu yanaşma Go dilini xüsusilə platformadan asılı olmayan tətbiqlər və mikroservislər üçün çox uyğun edir.


# Values

Go proqramlaşdırma dilində **dəyərlər** dəyişənlər tərəfindən daşınan məlumatlardır. Bu dəyərlər sabit və ya dəyişən ola bilər və müxtəlif məlumat növlərini təmsil edir. Bu məlumat növlərini başa düşmək və düzgün istifadə etmək Go dilində proqramlaşdırmanın əsasını təşkil edir.

#### Əsas Məlumat Növləri

Go dilində bir neçə əsas məlumat növü mövcuddur:

1. **Ədədi Məlumat Növləri**:
   * **Tam Ədədlər**: Tam ədədləri təmsil etmək üçün istifadə olunur.
     * **İmzalı Tam Ədədlər**:
       * `int`: Platformaya görə dəyişə bilən ölçü (adətən 32 və ya 64 bit).
       * `int8`, `int16`, `int32`, `int64`: 8, 16, 32 və 64 bit ölçüləri ilə təyin edilir.
     * **İmzalı Olmayan Tam Ədədlər**:
       * `uint`: Platformaya görə dəyişə bilən ölçü (adətən 32 və ya 64 bit).
       * `uint8`, `uint16`, `uint32`, `uint64`: 8, 16, 32 və 64 bit ölçüləri ilə təyin edilir.
     * **`uintptr`**: Pointer dəyərlərini tam ədəd kimi saxlamaq üçün istifadə olunur.
   * **Onluq Ədədlər**: Onluq nöqtə olan ədədləri təmsil edir.
     * `float32`: Tək dəqiqlikli onluq ədəd.
     * `float64`: İkili dəqiqlikli onluq ədəd.
   * **Mürəkkəb Ədədlər**: Real və təsirli hissələri olan ədədləri təmsil edir.
     * `complex64`: `float32` real və təsirli hissələri olan mürəkkəb ədəd.
     * `complex128`: `float64` real və təsirli hissələri olan mürəkkəb ədəd.
   * **`byte`**: `uint8` üçün bir aliasdır və mətn simvollarını təmsil edir.
   * **`rune`**: `int32` üçün bir aliasdır və Unicode kod nöqtələrini təmsil edir.
2. **Boolean Məlumat Növü**:
   * **`bool`**: Yalnız iki dəyəri qəbul edə bilər: `true` (doğru) və `false` (yanlış). Məntiqi əməliyyatlar və proqramın idarə edilməsi üçün istifadə olunur.
3. **String Məlumat Növü**:
   * **`string`**: Mətn və ya simvol sətirlərini təmsil edir. Mətn əməliyyatları üçün istifadə olunur.
4. **Mürəkkəb Məlumat Növləri**:
   * **`struct`**: Müxtəlif növ məlumatları bir yerdə saxlamaq üçün istifadə olunan məlumat strukturu.
   * **`array`**: Eyni növ elementlərin sabit ölçüdə olan ardıcıllığını təmsil edir.
   * **`slice`**: Dinamik uzunluqda olan və array-ə çevik baxış təqdim edən növdür.
   * **`map`**: Açar-dəyər cütlərini saxlamaq üçün istifadə olunur.
   * **`channel`**: Go rutininin digər Go rutini ilə məlumat mübadiləsi üçün istifadə olunur.

#### Dəyişənlər və Tip Müəyyənləşdirmə

Go dilində bir dəyişən elan edildikdə, onun tipi müəyyən edilir və bu tip daha sonra dəyişdirilə bilmir. Bu, proqramın tip təhlükəsizliyini təmin edir və səhvlərin qarşısını alır. Aşağıdakı nümunə bu prinsipi göstərir:

```go
var age int
age = 32

age = "thirty-two" // compile error: cannot use "thirty-two" (type string) as type int in assignment
```

Burada `age` adlı dəyişən `int` tipi ilə elan edilir. İlk olaraq, `age` dəyişəninə `32` dəyəri təyin olunur, bu da düzgündür. Lakin, `"thirty-two"` adlı sətir tipli dəyəri `age` dəyişəninə təyin etməyə çalışdığınızda, bu, tərtib xətasına səbəb olur. Çünki `age` dəyişəni `int` tipi ilə təyin edildiyi üçün yalnız tam ədəd dəyərləri qəbul edə bilər.

#### Nəticə

Dəyərlər proqramlaşdırmada müxtəlif məqsədlər üçün istifadə olunur: məsələn, `int` dəyərini rəqəmsal əməliyyatlar üçün və `string` dəyərini mətn əməliyyatları üçün istifadə edə bilərik. Dəyərlərin doğru növlərinin seçilməsi proqramların düzgün işləməsini təmin edir.


# Variables

Dəyişənlər Go proqramlaşdırma dilində məlumatları saxlamaq üçün istifadə olunan əsas komponentlərdən biridir. Dəyişənlərə qiymət təyin etməklə, proqramın müxtəlif yerlərində həmin qiymətləri istifadə edə bilirik. Bu, proqramın dinamikliyini və çevikliyini artırır.

#### Dəyişənlərin Elan Edilməsi

Dəyişənlərin elan edilməsi üçün `var` açar sözündən istifadə olunur. Dəyişənin adı və məlumat tipi müəyyən edilir. Əgər dəyişənə ilkin dəyər verilməzsə, Go dilində dəyişənlər standart (default) dəyərlərlə təyin edilir. Məsələn, `int` tipli dəyişənlər üçün standart dəyər `0`, `string` tipli dəyişənlər üçün isə boş sətirdir.

**Nümunə:**

```go
var name string
name = "John Doe"
```

Bu nümunədə `name` adlı dəyişən `string` tipi ilə elan edilir. Daha sonra bu dəyişənə `"John Doe"` dəyəri təyin edilir. Bu, `name` dəyişəninin sətir tipli olduğunu göstərir.

#### Dəyişənlərin Dəyərinin Dəyişdirilməsi

Dəyişənlər proqramın icrası zamanı dəyişdirilə bilər. Lakin, dəyişənin tipi müəyyən edildikdən sonra, ona uyğun olmayan tipdəki dəyəri təyin etmək mümkün deyil.

**Nümunə:**

```go
var age int
age = 32

age = "thirty-two" // compile error: cannot use "thirty-two" (type string) as type int in assignment
```

Bu nümunədə `age` adlı dəyişən `int` tipi ilə elan edilir. İlk olaraq, `age` dəyişəninə `32` dəyəri verilir, bu, düzgündür. Lakin, daha sonra `"thirty-two"` adlı sətir tipi dəyəri `age` dəyişəninə təyin edilməyə çalışılır və bu, tərtib xətasına səbəb olur. Çünki `age` dəyişəni `int` tipinə malikdir və yalnız tam ədəd dəyərləri qəbul edə bilər.

#### Dəyişənlərin İstifadə Məqsədləri

Dəyişənlər proqramlarda müxtəlif məqsədlər üçün istifadə olunur. Məsələn, istifadəçinin adı, yaşı və digər məlumatları saxlamaq üçün dəyişənlərdən istifadə edə bilərik. Dəyişənlər proqramın müxtəlif hissələrində məlumatları saxlamaq və idarə etmək üçün əvəzsizdir.

**Misal:**

```go
var userName string
var userAge int

userName = "Alice"
userAge = 25
```

Bu nümunədə `userName` və `userAge` adlı dəyişənlər elan edilir. `userName` dəyişəninə `Alice` dəyəri, `userAge` dəyişəninə isə `25` dəyəri təyin edilir. Bu, istifadəçinin adını və yaşını saxlamaq üçün istifadə olunan dəyişənlərdir.

#### Nəticə

Dəyişənlər proqramlaşdırmanın əsasını təşkil edir. Onlar məlumatları saxlamaq, idarə etmək və proqramın müxtəlif hissələrində istifadə etmək üçün vacibdir. Dəyişənlərin tipi müəyyən edildikdən sonra, bu tipə uyğun olan dəyərlər ilə işləmək, proqramın düzgün işləməsini təmin edir.


# Assignment Operators

Assignment operators (təyin etmə operatorları) proqramlaşdırma dilində bir dəyişənə dəyər təyin etmək üçün istifadə olunur. Go dilində müxtəlif assignment operatorları mövcuddur və aşağıda onların əsas növləri göstərilmişdir:

1. **Assignment (`=`)**\
   Bu operator bir dəyişənə dəyər təyin edir. Əgər dəyişən daha əvvəl təyin edilməyibsə, əvvəlcə onu elan etməlisiniz.

   ```go
   var a int
   a = 10
   ```

   Burada, `a` dəyişəninə 10 dəyəri təyin edilir.
2. **Add and Assign (`+=`)**\
   Bu operator mövcud dəyişənin dəyərinə müəyyən bir dəyəri əlavə edir və nəticəni həmin dəyişənə təyin edir.

   ```go
   x := 5
   x += 3 // x = x + 3
   fmt.Println(x) // 8
   ```

   Burada, `x` dəyişəninə 3 əlavə edilir və nəticə `x`-ə təyin olunur.
3. **Subtract and Assign (`-=`)**\
   Bu operator mövcud dəyişənin dəyərindən müəyyən bir dəyəri çıxarır və nəticəni həmin dəyişənə təyin edir.

   ```go
   x := 10
   x -= 4 // x = x - 4
   fmt.Println(x) // 6
   ```

   Burada, `x` dəyişənindən 4 çıxarılır və nəticə `x`-ə təyin olunur.
4. **Multiply and Assign (`*=`)**\
   Bu operator mövcud dəyişənin dəyərini müəyyən bir dəyərlə vurur və nəticəni həmin dəyişənə təyin edir.

   ```go
   x := 6
   x *= 7 // x = x * 7
   fmt.Println(x) // 42
   ```

   Burada, `x` dəyişəni 7 ilə vurulur və nəticə `x`-ə təyin olunur.
5. **Divide and Assign (`/=`)**\
   Bu operator mövcud dəyişənin dəyərini müəyyən bir dəyərlə bölür və nəticəni həmin dəyişənə təyin edir.

   ```go
   x := 20
   x /= 4 // x = x / 4
   fmt.Println(x) // 5
   ```

   Burada, `x` dəyişəni 4-ə bölünür və nəticə `x`-ə təyin olunur.
6. **Modulus and Assign (`%=`)**\
   Bu operator mövcud dəyişənin dəyərini müəyyən bir dəyərlə modulyasiya edir (qalanı tapır) və nəticəni həmin dəyişənə təyin edir.

   ```go
   x := 17
   x %= 5 // x = x % 5
   fmt.Println(x) // 2
   ```

   Burada, `x` dəyişəninin 5-ə bölünməsi ilə qalan 2 olur və bu nəticə `x`-ə təyin olunur.

## Misal

Aşağıdakı kod parçaları müxtəlif assignment operatorlarının istifadəsini göstərir:

```go
package main

import "fmt"

func main() {
    a := 10
    fmt.Println("Initial value of a:", a)

    a += 5
    fmt.Println("After a += 5:", a) // 15

    a -= 3
    fmt.Println("After a -= 3:", a) // 12

    a *= 2
    fmt.Println("After a *= 2:", a) // 24

    a /= 4
    fmt.Println("After a /= 4:", a) // 6

    a %= 3
    fmt.Println("After a %= 3:", a) // 0
}
```

## Nəticə

Assignment operators Go proqramlaşdırma dilində dəyişənlərə dəyər təyin etməyə və bu dəyərləri dəyişməyə imkan verir. Bu operatorlar müxtəlif hesablama əməliyyatlarını sadə və qısa şəkildə yerinə yetirmək üçün istifadə olunur. Onların düzgün istifadəsi proqramın səmərəliliyini artırır və kodu daha oxunaqlı edir.


# Code Grouping Process

Kodların qruplaşdırılması prosesi çox sadədir. Bu proses sayəsində eyni dəyişənlər müxtəlif kod bloklarında fərqli şəkildə işləyə bilər. Go dilində kodları qruplaşdırmaq üçün süslü mötərizələrdən istifadə edilir. Aşağıda bunun necə həyata keçirildiyini nümunə ilə göstərək:

```go
package main

import "fmt"

func main() {
    variable := "one"
    {
		variable := "two"
        fmt.Println(variable)
    }
    fmt.Println(variable)
}
```

### Output:

```
two
one
```

Yuxarıda `variable` adlı bir dəyişən yaratdıq. Dəyişənin içindəki qiymət `"one"`dir. Daha sonra isə süslü mötərizələr arasında yenidən `variable` adlı bir dəyişən elan etdik və ona `"two"` qiymətini verdik. Bu iki dəyişən eyni ada malikdir, lakin fərqli kod bloklarında olduqları üçün bir-biri ilə əlaqəsi yoxdur. Hər biri öz blokunda müxtəlif dəyərlər daşıyır.

Bu prosesin daha yaxşı başa düşülməsi üçün hər dəyişənin yaddaş ünvanına baxaq. Pointerlərdən istifadə edərək, hər dəyişənin yaddaşdakı yerini öyrənək.

```go
package main

import "fmt"

func main() {
	variable := "one"
    {
		variable := "two"
        fmt.Println(variable)
        fmt.Println(&variable)  // Dəyişənin yaddaş ünvanı
    }
    fmt.Println(variable)
    fmt.Println(&variable)      // Yenə yaddaş ünvanı
}
```

### Output:

```
two
0xc00008c1d0
one
0xc00008c1c0
```

Gördüyünüz kimi, `variable` adlı dəyişən iki fərqli yaddaş ünvanında saxlanılır. Bu, hər iki dəyişənin eyni ada malik olmasına baxmayaraq, müxtəlif kod bloklarında fərqli yaddaş yerlərində saxlanıldığını göstərir.


# Type Conversion

Type conversion (tip çevirmə) Go dilində müxtəlif məlumat tipləri arasında çevirmələr etmək üçün istifadə olunur. Go, statik tipli bir dildir, bu səbəbdən bir tipdən digərinə çevirmə etmək üçün müvafiq çevirmə operatorlarından istifadə etməlisiniz.

## Tip Çevirmənin Əsasları

Go dilində tip çevirməsi bir dəyişənin bir tipdən digər tipə çevrilməsi üçün istifadə olunur. Çevirmə yalnız uyumlu tiplər arasında mümkündür. Tip çevirmə prosesində, konkret bir məlumat tipi digər bir tipə çevrilir, bu da müəyyən bir funksiyanı və ya əməliyyatı həyata keçirmək üçün lazım ola bilər.

## Tip Çevirmə Nümunələri

### Ən Sadə Tip Çevirmə

Aşağıdakı misalda, `int` tipində bir dəyişəni `float64` tipinə çevirmək göstərilir:

```go
package main

import "fmt"

func main() {
    var i int = 42
    var f float64 = float64(i) // int tipindən float64 tipinə çevirmə
    fmt.Println(f) // 42.0
}
```

Burada, `i` dəyişəni `int` tipindədir və onu `float64` tipinə çevirmək üçün `float64(i)` ifadəsi istifadə olunur.

### Stringə Çevirmə

Bir dəyişəni `string` tipinə çevirmək üçün `string()` funksiyasından istifadə edilir:

```go
package main

import "fmt"

func main() {
    var i int = 123
    var s string = string(i) // int tipindən string tipinə çevirmə
    fmt.Println(s) // Əslində bu doğru nəticə verməyəcək, çünki bu ASCII kodunu alacaq.
}
```

Bu misalda, `int` tipindəki dəyişəni `string` tipinə çevirmək `string(i)` istifadə edilir, amma burada `i` dəyişəni ASCII kodu kimi təfsir olunur, bu səbəbdən nəticə gözlənilməz ola bilər.

### Numeric Tiplər Arasında Çevirmə

Fərqli numeric tiplər arasında çevirmə (məsələn, `int` və `float64` arasında) `float64` kimi daha geniş tipdən daha dar tiplərə çevrilərkən məlumat itkisi ola bilər.

```go
package main

import "fmt"

func main() {
    var f float64 = 3.14159
    var i int = int(f) // float64 tipindən int tipinə çevirmə
    fmt.Println(i) // 3
}
```

Bu misalda, `float64` tipindəki dəyişəni `int` tipinə çevirmək `int(f)` istifadə edilir. Çevirmə prosesində ondalıklı hissə itirilir.

### Type Conversion with Functions

Dəyişənlərin və funksiyaların parametr tipləri arasında çevirmələr də edə bilərsiniz:

```go
package main

import (
    "fmt"
    "strconv"
)

func main() {
    var s string = "123"
    var i int
    var err error

    i, err = strconv.Atoi(s) // string tipindən int tipinə çevirmə
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println(i) // 123
}
```

Burada, `strconv.Atoi` funksiyası string tipindəki dəyəri `int` tipinə çevirmək üçün istifadə olunur.

## Nəticə

Go dilində tip çevirmə, fərqli məlumat tipləri arasında çevirmələr edərək müxtəlif əməliyyatları həyata keçirmək üçün əhəmiyyətlidir. Çevirmə prosesində məlumat itkisini və potensial səhvləri nəzərə almaq vacibdir. Tip çevirməni düzgün istifadə edərək, proqramınızı daha effektiv və çevik hala gətirə bilərsiniz.


# Comments

Şərhlər (comments) Go proqramlaşdırma dilində kodun oxunmasını asanlaşdırmaq, izah etmək və ya müəyyən qeydlər əlavə etmək üçün istifadə olunur. Şərhlər kompilyator tərəfindən nəzərə alınmır, yəni onlar proqramın icrasına təsir etmir. Go dilində iki növ şərh mövcuddur: tək sətrli şərhlər və çox sətrli şərhlər.

## Tək sətrli şərhlər

Tək sətrli şərhlər `//` simvolları ilə başlayır və sətirin qalan hissəsi şərh olaraq qəbul edilir. Bu tip şərhlər adətən kodun müəyyən hissəsini qısaca izah etmək üçün istifadə olunur.

```go
package main

import "fmt"

func main() {
    // Bu tək sətrli şərhdir və "Hello, World!" mesajını çap edəcək.
    fmt.Println("Hello, World!")
}
```

Yuxarıdakı nümunədə `//` simvollarından sonra gələn hissə şərh olaraq qəbul edilir və proqramın işləmə prosesinə təsir etmir.

## Çox sətrli şərhlər

Çox sətrli şərhlər `/*` ilə başlayır və `*/` ilə bitir. Bu şərhlər birdən çox sətirdə yazıla bilər və daha ətraflı izahlar üçün istifadə olunur.

```go
package main

import "fmt"

func main() {
    /*
       Bu çox sətrli şərhdir.
       Aşağıdakı kod konsolda "Hello, World!" mesajını çap edəcək.
    */
    fmt.Println("Hello, World!")
}
```

Bu nümunədə, `/*` və `*/` arasında olan bütün mətn çox sətrli şərh kimi qəbul edilir.

## Şərhlərin istifadə məqsədləri

1. **Kodun izah edilməsi:** Kodun funksionallığını, mürəkkəb hissələrini izah etmək üçün istifadə olunur.
2. **Gələcəkdə işarələmə:** Şərhlər gələcəkdə kodda hansı dəyişikliklər və ya təkmilləşdirmələrin edilməsi lazım olduğunu qeyd etmək üçün istifadə edilə bilər.
3. **Debugging:** Debugging zamanı müəyyən bir kod hissəsinin müvəqqəti olaraq şərh edilməsi və proqramın o hissəsinin icra edilməməsi təmin edilə bilər.

### Yaxşı şərh yazma təcrübələri

* **Anlayışlı və qısa olun:** Şərhlər lazımsız detallara girmədən kodu izah etməlidir.
* **Yenilənmiş saxlayın:** Kodun funksionallığı dəyişdikdə, şərhləri də ona uyğun şəkildə yeniləmək vacibdir. Əks halda, şərhlər köhnəlmiş ola bilər və çaşqınlığa səbəb ola bilər.
* **Çoxlu şərhlərdən çəkinin:** Şərhlər faydalı olmalı, lakin kodu şərhlərlə doldurmaq kodun oxunmasını çətinləşdirə bilər.

### Nəticə

Go dilində şərhlər kodun təmiz və başa düşülən olmasına kömək edir. Hər iki şərh növü, yəni tək sətrli və çox sətrli şərhlər, proqramlaşdırma zamanı daha səliqəli və başadüşülən kod yazmağa imkan verir.


# Arithmetic Operators

Go proqramlaşdırma dilində aritmetik operatorlar ədədi dəyərlər üzərində riyazi əməliyyatlar aparmaq üçün istifadə olunur. Bu operatorlar cəmləmə, çıxma, vurma, bölmə və qalıq kimi əməliyyatları həyata keçirir. Aritmetik operatorlar əsasən integer (tam ədədlər), float (ondalıq ədədlər) kimi növlər üzərində işləyir.

Aşağıda Go dilində mövcud olan əsas aritmetik operatorlar və onların istifadəsi haqqında məlumat verilib.

## Cəmləmə operatoru (`+`)

Cəmləmə operatoru iki ədədin cəmini hesablayır.

```go
package main

import "fmt"

func main() {
    a := 10
    b := 5
    c := a + b // Cəmləmə əməliyyatı: 10 + 5 = 15
    fmt.Println("Cəmi:", c)
}
```

Yuxarıdakı nümunədə, `a + b` əməliyyatı iki tam ədədin cəmini hesablayır və nəticə `c` dəyişəninə təyin olunur.

## Çıxma operatoru (`-`)

Çıxma operatoru iki ədədin fərqini hesablayır.

```go
package main

import "fmt"

func main() {
    a := 10
    b := 5
    c := a - b // Çıxma əməliyyatı: 10 - 5 = 5
    fmt.Println("Fərq:", c)
}
```

Bu nümunədə, `a - b` əməliyyatı ilə iki tam ədədin fərqi hesablanır.

## Vurma operatoru (`*`)

Vurma operatoru iki ədədin hasilini hesablayır.

```go
package main

import "fmt"

func main() {
    a := 10
    b := 5
    c := a * b // Vurma əməliyyatı: 10 * 5 = 50
    fmt.Println("Hasil:", c)
}
```

Bu nümunədə, `a * b` iki ədədin hasilini hesablayır.

## Bölmə operatoru (`/`)

Bölmə operatoru iki ədədin bölünməsini həyata keçirir. Lakin, Go dilində tam ədədlərlə bölmə zamanı nəticə tam ədəd olur. Əgər dəqiq nəticə istənilirsə, ondalıq növlərdən (`float32`, `float64`) istifadə edilməlidir.

```go
package main

import "fmt"

func main() {
    a := 10
    b := 5
    c := a / b // Bölmə əməliyyatı: 10 / 5 = 2
    fmt.Println("Nəticə:", c)

    x := 10.0
    y := 4.0
    z := x / y // Ondalıq ədədlər üzərində bölmə: 10.0 / 4.0 = 2.5
    fmt.Println("Ondalıq nəticə:", z)
}
```

Bu nümunədə, tam ədədlərlə bölmə nəticəsi tam ədəd kimi göstərilir, ondalıq növlərlə bölmə isə dəqiq nəticə verir.

## Qalıq operatoru (`%`)

Qalıq operatoru iki ədədin bölünməsindən qalan qalıq dəyərini qaytarır.

```go
package main

import "fmt"

func main() {
    a := 10
    b := 3
    c := a % b // Qalıq əməliyyatı: 10 % 3 = 1
    fmt.Println("Qalıq:", c)
}
```

Bu nümunədə, `a % b` əməliyyatı 10-un 3-ə bölünməsindən sonra qalan qalıq olan `1` dəyərini qaytarır.

## Nəticə

Aritmetik operatorlar Go proqramlaşdırma dilində ədədi dəyərlər üzərində əsas riyazi əməliyyatları aparmaq üçün istifadə olunur. Bu operatorlar proqramın müxtəlif hesablamalarında geniş istifadə edilir.


# Logical Operators

Go proqramlaşdırma dilində məntiqi operatorlar şərtlər üzərində məntiqi əməliyyatlar aparmaq üçün istifadə olunur. Bu operatorlar əsasən şərti ifadələrin qiymətləndirilməsi zamanı istifadə edilir və nəticə olaraq `true` və ya `false` (boolean) dəyərlər qaytarır.

Go dilində üç əsas məntiqi operator mövcuddur:

* **Və (AND) operatoru** `&&`
* **Və ya (OR) operatoru** `||`
* **Deyil (NOT) operatoru** `!`

Aşağıda bu operatorların istifadəsi haqqında məlumat verilib.

## Və (AND) Operatoru `&&`

Və operatoru iki şərti qiymətləndirir və hər iki şərt `true` olduqda nəticə olaraq `true` qaytarır. Əgər şərtlərdən biri belə `false` olarsa, nəticə `false` olur.

```go
package main

import "fmt"

func main() {
    a := true
    b := false

    // Və operatoru hər iki şərt doğru olduqda nəticə doğru olur
    result := a && b // Nəticə: false
    fmt.Println("a və b:", result)

    c := 10
    d := 20

    // Şərtlərdən hər ikisi doğru olduğu üçün nəticə true olur
    result = (c < d) && (c != d) // Nəticə: true
    fmt.Println("c < d və c != d:", result)
}
```

Yuxarıdakı nümunədə, `a && b` ifadəsi qiymətləndirildikdə nəticə `false` olur, çünki `b` dəyişəni `false` dəyərinə malikdir. İkinci nümunədə isə hər iki şərt doğru olduğundan nəticə `true` olur.

## Və ya (OR) Operatoru `||`

Və ya operatoru iki şərti qiymətləndirir və hər hansı bir şərt `true` olduqda nəticə olaraq `true` qaytarır. Hər iki şərt `false` olduqda isə nəticə `false` olur.

```go
package main

import "fmt"

func main() {
    a := true
    b := false

    // Və ya operatoru hər hansı bir şərt doğru olduqda nəticə doğru olur
    result := a || b // Nəticə: true
    fmt.Println("a və ya b:", result)

    c := 10
    d := 20

    // Şərtlərdən biri doğru olduğu üçün nəticə true olur
    result = (c == d) || (c < d) // Nəticə: true
    fmt.Println("c == d və ya c < d:", result)
}
```

Bu nümunədə `a || b` ifadəsi qiymətləndirildikdə, `a` dəyişəni `true` olduğu üçün nəticə `true` olur. İkinci nümunədə isə `c < d` şərti doğru olduğu üçün nəticə yenə `true` qaytarılır.

## Deyil (NOT) Operatoru `!`

Deyil operatoru tək bir şərti tərsinə çevirir. Əgər şərt `true` olarsa, `!` operatoru nəticəni `false` edəcək və əksinə, şərt `false` olarsa, nəticə `true` olacaq.

```go
package main

import "fmt"

func main() {
    a := true
    b := false

    // Deyil operatoru şərti tərsinə çevirir
    result := !a // Nəticə: false
    fmt.Println("!a:", result)

    result = !b // Nəticə: true
    fmt.Println("!b:", result)
}
```

Bu nümunədə `!a` ifadəsi qiymətləndirildikdə, `a` dəyişəni `true` olduğu üçün nəticə `false` olur. Eyni qaydada `!b` ifadəsi qiymətləndirildikdə, `b` dəyişəni `false` olduğu üçün nəticə `true` olur.

## Nəticə

Məntiqi operatorlar Go proqramlaşdırma dilində şərti ifadələri qiymətləndirmək üçün çox faydalıdır. `&&` (Və), `||` (Və ya), və `!` (Deyil) operatorları müxtəlif şərtlərin kombinasiya edilməsi və qiymətləndirilməsi üçün geniş istifadə edilir. Bu operatorlar if-else, dövrlər və digər məntiqi quruluşlar ilə sıx bağlıdır.


# Relational Operators

Relational operators (qarşılaşdırma operatorları) proqramlaşdırmada iki dəyəri müqayisə etmək üçün istifadə olunur. Go proqramlaşdırma dilində də relational operators mövcuddur və aşağıdakıları əhatə edir:

1. **Equal to (bərabərdir) `==`**\
   İki dəyərin bərabər olub-olmadığını yoxlayır. Əgər dəyərlər bərabərdirsə, `true`, əks halda `false` qaytarır.

   ```go
   a := 10
   b := 20
   fmt.Println(a == b) // false
   ```
2. **Not equal to (bərabər deyil) `!=`**\
   İki dəyərin bərabər olmadığını yoxlayır. Əgər dəyərlər bərabər deyilsə, `true`, əks halda `false` qaytarır.

   ```go
   a := 10
   b := 20
   fmt.Println(a != b) // true
   ```
3. **Greater than (böyükdür) `>`**\
   Bir dəyərin digərindən böyük olub-olmadığını yoxlayır. Əgər birinci dəyər ikinci dəyərdən böyükdürsə, `true`, əks halda `false` qaytarır.

   ```go
   a := 10
   b := 20
   fmt.Println(a > b) // false
   ```
4. **Less than (kiçikdir) `<`**\
   Bir dəyərin digərindən kiçik olub-olmadığını yoxlayır. Əgər birinci dəyər ikinci dəyərdən kiçikdirsə, `true`, əks halda `false` qaytarır.

   ```go
   a := 10
   b := 20
   fmt.Println(a < b) // true
   ```
5. **Greater than or equal to (böyük və ya bərabərdir) `>=`**\
   Bir dəyərin digərindən böyük və ya bərabər olub-olmadığını yoxlayır. Əgər birinci dəyər ikinci dəyərdən böyükdür və ya bərabərdirsə, `true`, əks halda `false` qaytarır.

   ```go
   a := 10
   b := 10
   fmt.Println(a >= b) // true
   ```
6. **Less than or equal to (kiçik və ya bərabərdir) `<=`**\
   Bir dəyərin digərindən kiçik və ya bərabər olub-olmadığını yoxlayır. Əgər birinci dəyər ikinci dəyərdən kiçikdir və ya bərabərdirsə, `true`, əks halda `false` qaytarır.

   ```go
   a := 10
   b := 20
   fmt.Println(a <= b) // true
   ```

## Misal

Aşağıdakı kod parçaları relational operatorlardan istifadəni göstərir:

```go
package main

import "fmt"

func main() {
    x := 15
    y := 20

    fmt.Println("x == y:", x == y) // false
    fmt.Println("x != y:", x != y) // true
    fmt.Println("x > y:", x > y)   // false
    fmt.Println("x < y:", x < y)   // true
    fmt.Println("x >= y:", x >= y) // false
    fmt.Println("x <= y:", x <= y) // true
}
```

## Nəticə

Relational operators proqramlaşdırma dilində dəyərlərin müqayisə edilməsi üçün istifadə olunur. Bu operatorlar müxtəlif şərtləri yoxlamağa kömək edir və şərtli ifadələrdə geniş istifadə olunur. Go dilində bu operatorların doğru istifadə edilməsi proqramların daha düzgün və effektiv işləməsini təmin edir.


# Constants

Sabitlər (constants) Go proqramlaşdırma dilində proqramın heç bir yerində dəyişdirilə bilməyən sabit dəyərlərdir. Sabitlər bir dəfə müəyyən edilir və sonra proqramın istənilən yerində istifadə edilə bilər.

Sabitlər const açar sözü ilə müəyyən edilir və onların məlumat növləri göstərilir. Sabitlər işə salınmalıdır və sonra dəyişdirilə bilməz.

```go
const pi = 3.14159
const welcomeMessage = "Welcome to Go programming"
```

Bu nümunədə, sabit pi float64 növü kimi müəyyən edilir və 3.14159-a mənimsədilir. Sonra, welcomeMessage sabiti sətir kimi müəyyən edilir və "Welcome to Go programming" olaraq işə salınır.

Sabitlər müəyyən bir məqsəd üçün istifadə ediləcək proqramların sabit dəyərlərini saxlamaq üçün istifadə olunur. Məsələn, pi sayı və ya xüsusi mesaj və ya səhv kodu. Sabitlər proqramları daha oxunaqlı və davamlı etməyə kömək edir və proqramın müxtəlif yerlərində eyni sabitdən dəfələrlə istifadə etməyi asanlaşdırır.

Sabitlər proqramın istənilən yerində istifadə oluna bildiyi üçün onlar müxtəlif fayllar arasında da paylaşıla bilər. Bundan əlavə, sabitlərin dəyişdirilə bilməməsi proqram səhvlərini azaldır və təhlükəsizliyi artırır.


# if/else

if və else ifadələri, Go programlaşdırma dilində, müəyyən şərtlərin doğru və ya yanlış olduğu hallarda fərqli kod bloklarının işləməsini həyata keçirir.

```go
if x > 0 {
    fmt.Println("Positive number")
} else if x < 0 {
    fmt.Println("Negative number")
} else {
    fmt.Println("Zero")
}
```

Bu nümunədə, if ifadəsi, x dəyişkənin 0 dan böyük olması halında "Positive number" mətnini yazdıracaq. Əgər x dəyişəni 0 dan böyük deyilsə, else if ifadəsi yoxlanılacaq ve x dəyişəninin 0 dan kiçik olması halında "Negative number" mətnini yazdıracaq. Əgər x dəyişəni 0 dan böyük və ya kiçik deyilsə, else bloku işləyəcək ve "Zero" mətnini yazdıracaqdır.

```go
//if və else ifadələri, qarışıq şərtləri idarə etmək üçün də istifadə oluna bilər.
if x > 10 && x < 20 {
    fmt.Println("x is between 10 and 20")
} else if x > 20 && x < 30 {
    fmt.Println("x is between 20 and 30")
} else {
    fmt.Println("x is not between 10 and 30")
}
```

Bu nümunədə, if ifadəsi, x dəyişənin 10 ve 20 arasında olması halında "x is between 10 and 20" mətnini yazdıracaqdır. Əgər x dəyişəni 10 ve 20 arasında deyilsə, else if ifadesi yoxlanılacaq və x dəyişənin 20 və 30 arasında olması halında "x is between 20 and 30" mətnini yazdıracaqdır. Əgər x dəyişəni 10 və 30 arasında deyilsə, else bloku işələyəcəkdir və "x is not between 10 and 30" mətnini yazdıracaqdır.


# For

for dövür operatırı, Go programlasdırma dilində, müəyyən bir şərt doğru olduğu halda təkrarlanan kod bloklarını ifadə etmək üçün istifadə olunur. for dövrü, bir başlanğıc halı, bir şərt və bir addım dəyərinə sahibdir.

```go
for i := 0; i < 5; i++ {
    fmt.Println(i)
}
```

Bu nümunədə, for dövrü, i dəyişəninin 0 dəyərindən başlayaraq, i dəyişəni < 5 şərtini ödədiyi müddətcə təkrarlanan bir dövrdür. Dövrdəki hər adımdda i dəyişəni 1 artırılır və nəticədə i dəyəri 4 olur.

for dövrü, şərt hissəsi doğru olana qədər təkrarlanır. Şərt hissəsi doğru olmadığında, dövr sonlanır. Ayrıca, for dövrü break və ya continue ifadələri ilə də idarə edilə bilir.

```go
for i := 0; i < 10; i++ {
    if i == 3 {
        break
    }
    if i%2 == 0 {
        continue
    }
    fmt.Println(i)
}
```

Bu nümunədə, for dövrü, i dəyişəninin 0 dəyərindən başlayaraq, i dəyişəni < 10 şərtini ödəyənədək təkrarlanan bir dövrdür. Dövrdəki hər adımdda, i dəyişəni 1 artırılır.

Dövr, if şərti ilə kəsilir ve break ifadesi işə düşür. Ayrıca, i dəyəri 3 olduğunda da dövr sonlanır.

Dövr içindəki digər if şərti isə, əgər i dəyəri cüt ədəddirsə bir sonrakı addıma keçərək i dəyərini yazdırmaz. Bu şərtin səbəbi, sadəcə tək ədədləri yazdırmaq istəməmizdir.

Sonsuz dövr yaratmaq üçün aşağıdakı nümunəyə baxaq.

```go
i:=0
for  {
    if i == 100 {
        break
    }
    fmt.Println(i)
	
	i++
}
```

Bu nümunədə i dəyişənimizə 0 dəyərini mənimsədirik. Daha sonra dövr daxilində i əgər 100-ə bərabər olarsa break ifadəsi ilə dövrü sonlandırırıq, əks halda dövr davam edəcəkdir


# Switch

Switch ifadəsi müəyyən şərtlər əsasında müxtəlif əməliyyatları yerinə yetirmək üçün Go proqramlaşdırma dilində istifadə olunur:

```go
day := "sunday"

switch day {
case "monday":
    fmt.Println("Today is Monday")
case "tuesday":
    fmt.Println("Today is Tuesday")
case "wednesday":
    fmt.Println("Today is Wednesday")
case "thursday":
    fmt.Println("Today is Thursday")
case "friday":
    fmt.Println("Today is Friday")
case "saturday":
    fmt.Println("Today is Saturday")
case "sunday":
    fmt.Println("Today is Sunday")
default:
    fmt.Println("Invalid day")
}
```

Bu misalda switch ifadəsi gün dəyişəninin dəyərindən asılı olaraq müxtəlif əməliyyatlar yerinə yetirir. Gün dəyişəni "monday"dirsə, "Today is Monday" mətnini çap edir. Gün dəyişəni "tuesday"dırsa, "Today is Tuesday" mətnini çap edir. Bu şəkildə, gün dəyişəninin dəyərindən asılı olaraq müxtəlif kod blokları icra edilə bilər.

default ifadəsi bütün şərtlər doğru olmadığı təqdirdə işləyəcək kod blokuna aiddir.

```go
switch x {
case 1:
    fmt.Println("x is 1")
case 2:
    fmt.Println("x is 2")
case 3:
    fmt.Println("x is 3")
default:
    fmt.Println("x is not 1, 2 or 3")
}
```

Bu misalda switch ifadəsi x dəyişəninin qiymətindən asılı olaraq müxtəlif əməliyyatlar yerinə yetirir. Əgər x dəyişəni 1-dirsə, o, "x-1-dir" mətnini çap edir. Əgər x dəyişəni 2-dirsə, o, "x-2-dir" mətnini çap edir. Əgər x dəyişəni 3-dürsə, o, “x-3-dür” mətnini çap edir. Əgər x dəyişəni 1, 2 və ya 3 deyilsə, standart blok işləyəcək və "x is not 1, 2 or 3" mətnini çap edəcək.


# Arrays

Array , Go proqramlaşdırma dilində eyni tipdə bir neçə dəyişəni tək bir dəyişkəndə saxlamağa imkan verən bir məlumat strukturudur. Bir array əvvəlcədən müəyyən edilmiş ölçüyə və müəyyən bir məlumat tipinə malikdir.

```go
var a [5]int
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
fmt.Println(a)
```

Bu nümunədə, a adlı bir array müəyyən edilir və ölçüsü 5 olaraq təyin edilir. Array-in elementlərinə a\[index] sintaksisi ilə daxil olmaq olar və elementlər tək-tək təyin edilə bilər. Bu nümunədə, array elementlərinə müvafiq olaraq 1, 2, 3, 4 və 5 dəyərləri təyin edilir və fmt.Println(a) ifadəsi istifadə edilərək bütün array-in elementləri ekrana çıxarılır.

Array-lərin ölçüsü bir dəfə təyin edildikdən sonra dəyişdirilə bilməz. Ancaq Go dilindəki dil xüsusiyyətləri ilə array ölçüləri dəyişdirilə bilər. Bu xüsusiyyət, Go dilində slice adlanan bir məlumat strukturu ilə həyata keçirilir.

```go
a := [5]int{1, 2, 3, 4, 5}
fmt.Println(a)
```

Bu nümunədə, a adlı bir array müəyyən edilir və elementləri {1, 2, 3, 4, 5} olaraq təyin edilir. Array-in ölçüsü elementlərin sayına bərabər olmalıdır. Bu nümunədə, a adlı array {1, 2, 3, 4, 5} dəyərləri ilə başlatılır və bütün elementləri ekrana çıxarılır.


# Slices

Dilimin əsas məlumat strukturlarından biri olan slices, Go proqramlaşdırma dilində ölçüsü dəyişdirilə bilən, elastik bir array məlumat strukturdur. Slices, array-lərin bir alt hissəsi kimi qəbul edilə bilər və Go dilində tez-tez istifadə olunur.

```go
a := []int{1, 2, 3, 4, 5}
fmt.Println(a)
```

Bu nümunədə, a adlı bir dilim təyin edilir və elementləri {1, 2, 3, 4, 5} olaraq təyin edilir. Array-lərdən fərqli olaraq, dilimlərin ölçüsü təyin edilmə mərhələsində göstərilmir. Slices boş da təyin edilə bilər.

```go
var a []int
a = append(a, 1)
a = append(a, 2, 3, 4, 5)
fmt.Println(a)
```

Bu nümunədə, a adlı bir dilim təyin edilir və append() funksiyası ilə elementlərinə müvafiq olaraq 1, 2, 3, 4 və 5 dəyərləri əlavə edilir. append() funksiyası, bir dilimə bir və ya bir neçə element əlavə etmək üçün istifadə olunur.

Dilimlər, array-lərdən fərqli olaraq, bir alt hissə kimi də təyin edilə bilər.

```go
a := []int{1, 2, 3, 4, 5}
b := a[1:4]
fmt.Println(b)
```

Bu nümunədə, a adlı bir dilim təyin edilir və elementlərinə {1, 2, 3, 4, 5} dəyərləri verilir. b adlı bir dilim isə a diliminin 1-ci indeksindən 4-cü indeksinə qədər olan alt hissəsini əhatə edir. b dilimindəki elementlər {2, 3, 4} olaraq göstərilir.


# Maps

Map, Go proqramlaşdırma dilində bir açar-dəyər cütləri kolleksiyasıdır. Map məlumat strukturu, digər proqramlaşdırma dillərindəki dictionary, hash table və ya associative array kimi məlumat strukturlarına bənzəyir. Bir Map məlumat strukturu, müəyyən bir açar üçün bir dəyər saxlayır.

```go
var colors map[string]string
colors = make(map[string]string)
colors["red"] = "#FF0000"
colors["green"] = "#00FF00"
colors["blue"] = "#0000FF"
fmt.Println(colors)
```

Bu nümunədə, colors adlı bir Map təyin edilir və make() funksiyası ilə yaradılır. colors məlumat strukturu red, green və blue açarlarına sahib rəng kodları ilə doldurulur və fmt.Println(colors) ifadəsi istifadə edilərək Map məlumat strukturundakı bütün açar-dəyər cütləri ekrana çıxarılır.

Map məlumat strukturu, digər proqramlaşdırma dillərindəki məlumat strukturlarından fərqli olaraq, açarlar və dəyərlər üçün müəyyən bir məlumat tipini göstərmək məcburiyyətində deyil. Açarlar və dəyərlər fərqli məlumat tiplərində ola bilər.

```go
ages := map[string]int{
    "Alice": 25,
    "Bob":   30,
    "Carol": 35,
}
fmt.Println(ages)
```

Bu nümunədə, ages adlı bir Map təyin edilir və string tipində açarlar və int tipində dəyərlərlə əlaqəli cütlər təyin olunur. Map məlumat strukturunun yaradılması və elementlərin əlavə edilməsi make() funksiyası ilə birləşdirilərək də həyata keçirilə bilər.


# Range

range ifadəsi, Go proqramlaşdırma dilində müəyyən bir array, dilim və ya map üzərində dövr yaratmaq üçün istifadə olunur. range ifadəsi, məlumat strukturundakı bütün elementləri tək-tək götürmək üçün istifadə olunur.

```go
numbers := []int{1, 2, 3, 4, 5}
for i, num := range numbers {
    fmt.Println("index:", i, "number:", num)
}
```

Bu nümunədə, numbers adlı bir dilim təyin edilir və elementlərinə {1, 2, 3, 4, 5} dəyərləri verilir. range ifadəsi istifadə edilərək, numbers dilimindəki bütün elementlər tək-tək götürülür və for dövrü içində istifadə olunur. i dəyişəni dövrün sırasındakı indeks dəyərini, num dəyişəni isə indeksdəki elementi təmsil edir. Bu nümunədə, indekslər və elementlər birgə yazdırılır.

```go
colors := map[string]string{
    "red":   "#FF0000",
    "green": "#00FF00",
    "blue":  "#0000FF",
}
for color, code := range colors {
    fmt.Println("color:", color, "code:", code)
}
```

Bu nümunədə, colors adlı bir map təyin edilir və red, green və blue açarlarına sahib rəng kodları verilir. range ifadəsi istifadə edilərək, colors xəritəsindəki bütün açar-dəyər cütləri tək-tək götürülür və for dövrü içində istifadə olunur. color dəyişəni açarı, code dəyişəni isə açara uyğun dəyəri təmsil edir. Bu nümunədə, rənglər və onların kodları birgə yazdırılır.


# Functions

Funksiya, Go proqramlaşdırma dilində, müəyyən bir vəzifəni yerinə yetirən kod bloklarını ifadə edir. Bir funksiya, bir və ya bir neçə parametr ala bilər, bir əməliyyatı həyata keçirə bilər və bir və ya bir neçə nəticə qaytara bilər.

```go
func add(a int, b int) int {
return a + b
}
​
result := add(5, 10)
fmt.Println(result)
```

Bu nümunədə, add adlı bir funksiya təyin edilir. Funksiya, a və b adlı iki int tipində parametr qəbul edir və bu parametrlər toplanaraq nəticə int tipində geri qaytarılır. add funksiyası, 5 və 10 parametrləri ilə çağırılır və nəticə fmt.Println(result) ifadəsi ilə yazdırılır.

```go
func swap(a, b string) (string, string) {
    return b, a
}
​
x, y := swap("hello", "world")
fmt.Println(x, y)
```

Bu nümunədə, swap adlı bir funksiya təyin edilir. Funksiya, a və b adlı iki string tipində parametr qəbul edir və bu parametrləri bir-biri ilə dəyişdirərək geri qaytarır. swap funksiyası, "hello" və "world" parametrləri ilə çağırılır və geri qaytarılan nəticələr x və y dəyişənləri tərəfindən qəbul edilir. Nəticələr fmt.Println(x, y) ifadəsi ilə yazdırılır.


# Variadic function

Variadic funksiyalar, Go proqramlaşdırma dilində dəyişən sayda arqument qəbul edən funksiyalardır. Bu funksiyalar bir və ya daha çox arqument qəbul edə bilər və arqumentlərin sayını dəyişən olaraq təyin etməyə imkan verir.

```go
func sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

fmt.Println(sum(1, 2, 3, 4, 5))
fmt.Println(sum(2, 4, 6))
```

Bu nümunədə, sum adlı bir funksiya təyin edilir. Funksiya, nums adlı int tipində dəyişən sayda parametr qəbul edir və onların cəmini qaytarır. Funksiyanın daxilində for dövrü istifadə edərək, nums dilimindəki bütün elementlərin cəmi hesablanır. Funksiya sum(1, 2, 3, 4, 5) və sum(2, 4, 6) şəklində çağırılır və nəticələr ekrana yazdırılır.

```go
func concatenate(sep string, strs ...string) string {
    result := ""
    for i, str := range strs {
        if i > 0 {
            result += sep
        }
        result += str
    }
    return result
}

fmt.Println(concatenate(", ", "foo", "bar", "baz"))
fmt.Println(concatenate("-", "hello", "world"))
```

Bu nümunədə, concatenate adlı bir funksiya təyin edilir. Funksiya, sep adlı string tipində bir parametr və strs adlı dəyişən sayda string tipində parametrlər qəbul edir. Funksiyanın daxilində for dövrü istifadə edilərək, strs dilimindəki bütün elementlər birləşdirilir və sep ayracı ilə birgə yazılır. Funksiya concatenate(", ", "foo", "bar", "baz") və concatenate("-", "hello", "world") şəklində çağırılır və nəticələr ekrana yazdırılır.


# Closures

Closures, Go proqramlaşdırma dilində, bir funksiyanın başqa bir funksiyanın daxilində yaradılması ilə əmələ gələn bir strukturdur. Bu struktur, bir funksiyanın daxilində olan başqa bir funksiyaya istinad edərək, funksiyanın işlədiyi mühitin xaricindəki dəyişənlərə giriş imkanı verir.

```go
func outer() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

increment := outer()
fmt.Println(increment())
fmt.Println(increment())
fmt.Println(increment())
```

Bu nümunədə, `outer` adlı bir funksiya təyin edilir. Funksiya, bir daxili funksiya qaytarır və daxili funksiya, `count` adlı bir dəyişənə giriş imkanı əldə edir. `increment` adlı bir dəyişənə `outer()` funksiyası təyin edilir və bu dəyişən vasitəsilə daxili funksiya işlədilir. `count` dəyişəni hər dəfə `increment()` çağırıldıqda artır və hər çağırışda artım ekrana yazdırılır.

```go
func adder(a int) func(int) int {
    return func(b int) int {
        return a + b
    }
}

addFive := adder(5)
fmt.Println(addFive(2))
fmt.Println(addFive(3))
```

Bu nümunədə, adder adlı bir funksiya təyin edilir. Funksiya, a adlı bir int tipində parametr qəbul edir və bir daxili funksiya qaytarır. Daxili funksiya, b adlı bir int tipində parametr qəbul edir və a ilə b parametrlərinin cəmini geri qaytarır. addFive adlı bir dəyişənə adder(5) funksiyası təyin edilir və bu dəyişən ilə daxili funksiya işlədilir. addFive(2) və addFive(3) çağırıldıqca nəticələr ekrana yazdırılır.


# Anonymous

Anonymous functions (anonim funksiyalar) Go proqramlaşdırma dilində adlandırılmamış funksiyalardır. Bu funksiyalar ad vermədən yaradılır və lazım olduqda birbaşa istifadə olunur. Anonymous funksiyalar adətən bir dəfəlik əməliyyatlar üçün və ya daha çevik funksiyaların yaradılması üçün istifadə olunur.

## Anonymous Functionların İstifadəsi

Anonymous funksiyalar Go-da çox məqsədli istifadə üçün uyğundur. Onlar kodun daha səliqəli və funksional olmasını təmin edir, həmçinin funksiya içində lokal olaraq müəyyənləşdirilən dəyişənlərin və məntiqin istifadəsinə imkan tanıyır.

### Basic Syntax

Anonymous funksiyanın yaradılması və çağırılması sintaksisi aşağıdakı kimidir:

```go
package main

import "fmt"

func main() {
    // Anonymous funksiyanın yaradılması və birbaşa icrası
    func() {
        fmt.Println("Hello from anonymous function!")
    }()
}
```

Bu nümunədə, anonim funksiya dərhal çağırılır və "Hello from anonymous function!" mesajını ekrana yazdırır.

### Parameters və Return Values ilə Anonim Funksiyalar

Anonymous funksiyalar həmçinin parametr qəbul edə və dəyər qaytara bilər:

```go
package main

import "fmt"

func main() {
    // Anonim funksiya ilə toplama əməliyyatı
    sum := func(a int, b int) int {
        return a + b
    }
    
    result := sum(3, 4)
    fmt.Println("Sum:", result) // Sum: 7
}
```

Bu nümunədə, anonim funksiya `a` və `b` parametrlərini qəbul edir və onların cəmini qaytarır. Bu funksiya `sum` adlı dəyişəndə saxlanılır və sonra çağırılır.

### Closures ilə Anonim Funksiyalar

Anonymous funksiyalar closures (bağlanma) kimi də istifadə edilə bilər. Bu, funksiyanın yaradıldığı mühitdəki dəyişənlərə daxil olmaq imkanı verir:

```go
package main

import "fmt"

func main() {
    // Bağlanma nümunəsi
    increment := func(x int) func() int {
        return func() int {
            x++
            return x
        }
    }
    
    counter := increment(0) // counter dəyişəni anonim funksiya alır
    
    fmt.Println(counter()) // 1
    fmt.Println(counter()) // 2
    fmt.Println(counter()) // 3
}
```

Bu nümunədə, `increment` anonim funksiyası digər anonim funksiya qaytarır. Bu iç funksiyanın `x` dəyişəninə bağlandığına diqqət yetirin, beləliklə, `counter` funksiyası hər çağırıldığında `x` dəyişənini artırır.

### Anonim Funksiyaların Parametr Kimi İstifadəsi

Anonymous funksiyalar çox vaxt digər funksiyalara parametr kimi ötürülür:

```go
package main

import "fmt"

// Funksiya anonim funksiyanı parametr kimi qəbul edir
func execute(fn func(int) int, value int) {
    result := fn(value)
    fmt.Println("Result:", result)
}

func main() {
    // Anonim funksiyanın digər funksiya üçün parametr kimi istifadə olunması
    execute(func(x int) int {
        return x * x
    }, 5) // Result: 25
}
```

Bu nümunədə, `execute` funksiyası anonim funksiyanı `fn` parametri kimi qəbul edir və sonra həmin anonim funksiyanı çağırır.

## Nəticə

Anonymous funksiyalar Go dilində kodun daha modul və çevik olmasına kömək edir. Onlar qısa müddətli funksiyaları daha sadə şəkildə təyin etməyə imkan tanıyır və bağlanma, parametr kimi ötürmə və digər müasir proqramlaşdırma texnikalarını dəstəkləyir. Anonim funksiyalar Go proqramlarının daha funksional və səliqəli olmasına yardımçı olur.


# Recursion

Recursion, Go proqramlaşdırma dilində, bir funksiyanın özünü çağırmasıdır. Bu struktur, müəyyən bir şərt yerinə yetirilənə qədər funksiyanın təkrarlanaraq işləməsini təmin edir.

```go
func factorial(n int) int {
    if n == 0 {
        return 1
    }
    return n * factorial(n-1)
}

fmt.Println(factorial(5))
```

Bu nümunədə, factorial adlı bir funksiya təyin edilir. Funksiya, n adlı bir int tipində parametr qəbul edir və faktorialı hesablayır. Funksiya daxilində, if şərti istifadə edilərək n dəyərinin 0 olub-olmadığı yoxlanılır. Əgər n 0-dırsa, 1 qaytarılır. Əgər n 0 deyil, başqa bir dəyərdirsə, funksiya özünü yenidən çağıraraq faktorialı hesablayır. factorial(5) çağırıldıqda nəticə ekrana yazdırılır.

```go
func fibonacci(n int) int {
    if n < 2 {
        return n
    }
    return fibonacci(n-1) + fibonacci(n-2)
}

fmt.Println(fibonacci(10))
```

Bu nümunədə, fibonacci adlı bir funksiya təyin edilir. Funksiya, n adlı bir int tipində parametr qəbul edir və Fibonacci sayını hesablayır. Funksiya daxilində, if şərti istifadə edilərək n dəyərinin 2-dən kiçik olub-olmadığı yoxlanılır. Əgər n 2-dən kiçikdirsə, n dəyəri geri qaytarılır. Əks halda, funksiya özünü yenidən çağıraraq Fibonacci sayını hesablayır. fibonacci(10) çağırıldıqda nəticələr ekrana yazdırılır.


# Empty Identifiers

Go proqramlaşdırma dilində boş identifikator, `_` işarəsi ilə təmsil olunur və bu, dəyəri göz ardı etmək üçün istifadə olunur. Boş identifikator, müəyyən bir kontekstdə istifadə edilməyəcək dəyərləri nəzərə almadan, sadəcə yer tutucu kimi xidmət edir. Bu xüsusiyyət müxtəlif hallarda, məsələn, çoxlu geri dönüş dəyərləri ilə işləmək, dəyişənləri göz ardı etmək və daha çox istifadə olunur.

## Boş Identifikatorların Əsas İstifadə Sahələri

### 1. İstifadəsiz Geri Dönüş Dəyərlərini Gözardı Etmək

Go dilində funksiyalar bir neçə dəyər qaytara bilər və əgər siz bu dəyərlərin yalnız bəzilərini istifadə etmək istəyirsinizsə, `_` ilə istifadə etmədiyiniz dəyərləri gözardı edə bilərsiniz.

**Misal:**

```go
package main

import (
    "fmt"
    "strconv"
)

func main() {
    // Bir neçə geri dönüş dəyəri olan funksiya
    value, _ := strconv.Atoi("123") // Xəta dəyərini gözardı edirik

    fmt.Println(value) // Çıxış: 123
}
```

Bu misalda, `strconv.Atoi` funksiyası tam ədəd və xəta qaytarır. Xəta bu xüsusi istifadədə vacib deyil, ona görə `_` ilə gözardı edilir.

### 2. İstifadəsiz Dəyişənləri Gözardı Etmək

Dəyişənləri elan edib amma istifadə etmədikdə, onları `_`-ə təyin edərək, məqsədli olaraq istifadə etmədiyinizi göstərə bilərsiniz.

**Misal:**

```go
package main

import "fmt"

func main() {
    // İstifadəsiz dəyişən
    _, b := 10, 20

    fmt.Println(b) // Çıxış: 20
}
```

Burada, `a` dəyişəni `_`-ə təyin edilib və yalnız `b` istifadə olunur.

### 3. `range` Dövrlərində Dəyərləri Gözardı Etmək

`range` açar sözünü istifadə edərək dilimlər, xəritələr və ya kanallarla iterasiya edərkən, yalnız dəyərləri istifadə etmək və indeksləri və ya açarları gözardı etmək istəyə bilərsiniz. Bu halda `_` istifadə edərək lazımsız hissələri gözardı edə bilərsiniz.

**Misal:**

```go
package main

import "fmt"

func main() {
    meyvələr := []string{"Alma", "Banan", "Çiyələk"}

    for _, meyvə := range meyvələr {
        fmt.Println(meyvə)
    }
}
```

Bu döngüdə, indeks `_` ilə gözardı edilir və yalnız dəyər (`meyvə`) istifadə olunur.


# Pointers

Pointers, Go proqramlaşdırma dilində, bir dəyişənin yaddaş ünvanını saxlayan bir məlumat tipidir. Yaddaş ünvanı, dəyişənin yaddaşda yerləşdiyi yerdir.

```go
func zeroVal(val int) {
    val = 0
}

func zeroPtr(ptr *int) {
    *ptr = 0
}

x := 5
zeroVal(x)
fmt.Println(x)

y := 5
zeroPtr(&y)
fmt.Println(y)
```

Bu nümunədə, zeroVal adlı bir funksiya təyin edilir. Funksiya int tipində bir parametr qəbul edir və val dəyişəninin dəyərini 0 olaraq dəyişir, amma bu dəyişiklik orijinal dəyişənə təsir etmir. Digər tərəfdən, zeroPtr adlı bir funksiya isə bir pointer (\*int) qəbul edir və bu göstərici ilə göstərilən dəyişənin dəyərini 0 olaraq dəyişdirir. x dəyişəninə 5 dəyəri atanır və zeroVal(x) çağırılır, amma x dəyişməz. y dəyişəninə isə 5 dəyəri atanır və zeroPtr(\&y) çağırıldıqda, y 0 olur.

```go
func swap(a, b *int) {
    temp := *a
    *a = *b
    *b = temp
}

x := 5
y := 10
swap(&x, &y)
fmt.Println(x, y)
```

Bu nümunədə, swap adlı funksiya iki pointer (\*int) qəbul edir və həmin dəyişənlərin dəyərlərini bir-biri ilə dəyişir. x-ə 5 və y-ə 10 dəyəri verilir. swap(\&x, \&y) çağırıldıqdan sonra, x 10 olur, y isə 5 olur.


# Strings and Runes

Strings, Go proqramlaşdırma dilində, Unicode simvollarının birləşdirilməsi ilə yaradılan bir simvol sətiridir. Hər bir simvol 1-4 bayt arasında dəyişən ölçülərdə ola bilər.

```go
str := "hello"
fmt.Println(str)
fmt.Println(str[0])
fmt.Println(str[1:3])
```

Bu nümunədə, str adlı bir string dəyişəni təyin edilir və "hello" dəyəri verilir. str dəyişəni ekrana çap olunur, sonra isə str\[0] ifadəsi istifadə edilərək sətirin ilk simvolu olan "h" ekrana çap edilir. str\[1:3] ifadəsi istifadə edilərək sətirin ikinci və üçüncü simvolları olan "el" ekrana çap edilir.

```go
for i, r := range "hello" {
    fmt.Printf("%d: %s\n", i, string(r))
}
```

Bu nümunədə, for döngüsü istifadə edilərək range funksiyası ilə "hello" stringindəki hər bir simvola giriş edilir. Hər simvolun mövqeyi və dəyəri ekrana çap olunur. Runes, Go proqramlaşdırma dilində, bir Unicode simvolunun birləşməsini ifadə edən bir məlumat tipidir. Runes, 1-4 bayt arasında dəyişən ölçülərdə ola bilən simvolları təmsil etmək üçün istifadə olunur.

```go
str := "こんにちは"
for i, r := range str {
    fmt.Printf("%d: %c\n", i, r)
}
```

Bu nümunədə, str adlı bir string dəyişəni təyin edilir və "こんにちは" dəyəri verilir. for döngüsü istifadə edilərək range funksiyası ilə hər bir simvola giriş edilir və simvolun mövqeyi və dəyəri ekrana çap olunur.


# Structs

Structlar, Go proqramlaşdırma dilində, fərqli məlumat tiplərini özündə birləşdirən bir məlumat quruluşudur. Bu quruluşda, müxtəlif məlumat tiplərinə sahib məlumatları bir arada saxlaya və bu məlumatlar üzərində əməliyyatlar apara bilərsiniz.

```go
type Person struct {
    Name string
    Age  int
}

var p Person
p.Name = "John Doe"
p.Age = 42
fmt.Println(p)
fmt.Printf("Name: %s, Age: %d\n", p.Name, p.Age)
```

Bu nümunədə, Person adlı bir struct yaradılır və Name adlı bir string tipində və Age adlı bir int tipində iki xüsusiyyət təyin edilir. var açar sözü ilə p adlı bir Person tipində dəyişən təyin edilir. p dəyişəninin Name və Age xüsusiyyətləri, p.Name və p.Age ifadələri istifadə edilərək təyin edilir və fmt.Println() funksiyası ilə p dəyişəni ekrana çap edilir. Əlavə olaraq, p.Name və p.Age ifadələri ilə, Name və Age xüsusiyyətləri ayrıca ekrana çap edilir.

```go
type Rectangle struct {
    width  float64
    height float64
}

func (r Rectangle) area() float64 {
    return r.width * r.height
}

r := Rectangle{width: 3.0, height: 4.0}
fmt.Println(r.area())
```

Bu nümunədə, Rectangle adlı bir struct yaradılır və width adlı bir float64 tipində və height adlı bir float64 tipində iki xüsusiyyət təyin edilir. area adlı bir funksiya, Rectangle tipində bir parametr qəbul edir və düzbucaqlının sahəsini hesablayır. Funksiyanın qaytardığı nəticə düzbucaqlının sahəsidir. r adlı bir Rectangle dəyişəni yaradılır və width və height xüsusiyyətlərinə 3.0 və 4.0 dəyərləri verilir. r.area() ifadəsi istifadə edilərək, düzbucaqlının sahəsi hesablanır və nəticə ekrana çap edilir.


# Anonymous struct

Go dilində **struct** əlaqəli məlumatları qruplaşdırmaq üçün istifadə olunur. Adətən, struct-lar müəyyən bir adla elan olunur və bu, kod daxilində onlara müraciət etməyə və təkrar istifadə etməyə imkan verir. Lakin Go həmçinin **anonim struct**-ları da dəstəkləyir, yəni müəyyən olunmuş adı olmayan struct-lar.

Anonim struct-lar, yalnız müəyyən bir kontekstdə istifadə edilən, sadə və müvəqqəti strukturlar üçün faydalıdır. Onlar əsasən qısamüddətli məlumat strukturları üçün istifadə olunur.

### Anonim Struct Nümunəsi:

```go
package main

import "fmt"

func main() {
    person := struct {
        Name string
        Age  int
    }{
        Name: "Alice",
        Age:  30,
    }
	
    fmt.Println("Name:", person.Name)
    fmt.Println("Age:", person.Age)
}
```

### Output:

```
Name: Alice
Age: 30
```

Bu nümunədə, anonim struct "Name" və "Age" məlumatlarını saxlamaq üçün yaradılır və `person` dəyişəninə təyin edilir. Struct-ın adı yoxdur və yalnız bu kod blokunda istifadə olunur.


# Method

Metodlar, Go proqramlaşdırma dilində, bir məlumat quruluşuna məxsus əməliyyatları yerinə yetirmək üçün istifadə edilən funksiya növüdür. Bu əməliyyatlar, məlumat quruluşunun xüsusiyyətləri üzərində işləyərək nəticə yaradır.

```go
type Rectangle struct {
    width  float64
    height float64
}

func (r Rectangle) area() float64 {
    return r.width * r.height
}

func (r Rectangle) perimeter() float64 {
    return 2 * (r.width + r.height)
}

r := Rectangle{width: 3.0, height: 4.0}
fmt.Println(r.area())
fmt.Println(r.perimeter())
```

Bu nümunədə, `Rectangle` adlı bir `struct` yaradılır və `width` adlı bir `float64` tipində və `height` adlı bir `float64` tipində iki xüsusiyyət təyin olunur. `area` adlı funksiya, `Rectangle` tipində parametr qəbul edir və düzbucaqlının sahəsini hesablayır. Funksiyanın qaytardığı nəticə, düzbucaqlının sahəsidir. `perimeter` adlı funksiya da `Rectangle` tipində parametr alır və düzbucaqlının perimetrini hesablayır. Funksiyanın qaytardığı nəticə, düzbucaqlının perimetridir. `r` adlı bir `Rectangle` dəyişəni yaradılır və `width` və `height` xüsusiyyətlərinə 3.0 və 4.0 dəyərləri təyin olunur. `r.area()` və `r.perimeter()` ifadələrindən istifadə edərək düzbucaqlının sahəsi və perimetri hesablanır və nəticələr ekrana yazdırılır.

```go
type Person struct {
    Name string
    Age  int
}

func (p *Person) setName(name string) {
    p.Name = name
}

func (p *Person) setAge(age int) {
    p.Age = age
}

func (p Person) getName() string {
    return p.Name
}

func (p Person) getAge() int {
    return p.Age
}

p := Person{Name: "John Doe", Age: 42}
p.setName("Jane Doe")
p.setAge(35)
fmt.Printf("Name: %s, Age: %d\n", p.getName(), p.getAge())
```

Bu nümunədə, `Person` adlı bir `struct` yaradılır və `Name` adlı bir `string` tipində və `Age` adlı bir `int` tipində iki xüsusiyyət təyin olunur. `setName` adlı funksiya, `Person` tipində pointer (`*Person`) parametr qəbul edir və şəxsin adını dəyişir. `setAge` adlı funksiya, `Person` tipində pointer (`*Person`) parametr qəbul edir və şəxsin yaşını dəyişir. `getName` adlı funksiya, `Person` tipində parametr qəbul edir və şəxsin adını qaytarır. `getAge` adlı funksiya isə şəxsin yaşını qaytarır.

`p` adlı bir `Person` dəyişəni yaradılır və `Name` və `Age` xüsusiyyətlərinə "John Doe" və 42 dəyərləri təyin olunur. Daha sonra, `p.setName("Jane Doe")` və `p.setAge(35)` ifadələrindən istifadə edərək şəxsin adı və yaşı dəyişdirilir.


# Interface

Go dilində interfeys, bir və ya bir neçə metodun müəyyən imza dəstini təyin edən bir məlumat tipidir. Bu imza dəsti, bir məlumat tipinin hansı metodları tətbiq etməli olduğunu göstərir. Bu səbəbdən, interfeyslər, bir məlumat tipinin hansı xüsusiyyətlərə malik olduğunu təyin etmək üçün istifadə olunur.

Məsələn, bir fiqurun sahəsini hesablamaq üçün area adlı bir funksiya təyin etdiyinizi düşünün. Kvadrat, dairə və düzbucaqlı kimi müxtəlif fiqurlar area funksiyasını fərqli şəkildə tətbiq edirlər. Bu halda, hər bir fiqurun sahəsini hesablamaq üçün ayrı-ayrı funksiyalar təyin etmək əvəzinə, interfeys istifadə edərək hamısını eyni tipdə bir məlumatda toplaya bilərsiniz.

```go
package main

import (
	"fmt"
	"math"
)

type Shape interface {
	area() float64
}

type Circle struct {
	radius float64
}

type Rectangle struct {
	width, height float64
}

type Square struct {
	side float64
}

func (c Circle) area() float64 {
	return math.Pi * c.radius * c.radius
}

func (r Rectangle) area() float64 {
	return r.width * r.height
}

func (s Square) area() float64 {
	return s.side * s.side
}

func getArea(shape Shape) float64 {
	return shape.area()
}

func main() {
	circle := Circle{radius: 5}
	rectangle := Rectangle{width: 5, height: 10}
	square := Square{side: 5}

	fmt.Printf("Circle area: %f\n", getArea(circle))
	fmt.Printf("Rectangle area: %f\n", getArea(rectangle))
	fmt.Printf("Square area: %f\n", getArea(square))
}
```

Bu nümunədə, Shape adlı bir interfeys təyin olunur və area adlı bir funksiya imzası göstərilir. Circle, Rectangle və Square adlı üç fərqli struktura malik məlumat tipləri təyin olunur və hər biri area adlı funksiyanı həyata keçirir. getArea adlı bir funksiya təyin olunur və parametri Shape tipindədir. Bu funksiya, sahəsi hesablanacaq fiqur məlumatını alır və area funksiyasını çağıraraq fiqurun sahəsini hesablamağa imkan verir. main funksiyasında isə, nümunə olaraq circle, rectangle və square adlı üç fərqli dəyişən yaradılır. Bu dəyişənlərin hər biri getArea funksiyasına parametr olaraq verilir və hər bir fiqurun sahəsi hesablanaraq ekrana yazdırılır.

Output:

```go
Circle area: 78.539816
Rectangle area: 50.000000
Square area: 25.000000
```


# Dynamic Variables

Go 1.18 versiyasından etibarən, Go dilinə yeni bir tip sinonimi olan `any` əlavə edildi. `any`, əslində, `interface{}` boş interfeysinin sinonimidir və proqramın daha oxunaqlı olmasını təmin etmək məqsədini daşıyır. Yəni, `any` istifadə edərək, `interface{}`-nin gördüyü işləri daha sadə şəkildə ifadə edə bilərik.

## Boş Interfeys (`interface{}`) və `any`

Go-da dinamik dəyişənlər digər dinamik dillərdə olduğu kimi istifadə edilməsə də, `interface{}` və `any` tipləri ilə dinamikliyə nail ola bilirik. Bu tiplər vasitəsilə, bir dəyişənə müxtəlif tiplərdə dəyərlər təyin edə bilərik.

### `any` ilə Nümunə:

```go
package main

import "fmt"

func main() {
    var dynamicVar any
	
    dynamicVar = 42
    fmt.Println("Integer value:", dynamicVar)

    dynamicVar = "Hello, Go!"
    fmt.Println("String value:", dynamicVar)

    dynamicVar = 3.14
    fmt.Println("Float value:", dynamicVar)
}
```

### Output:

```
Integer value: 42
String value: Hello, Go!
Float value: 3.14
```

Bu nümunədə biz `any` tipindən istifadə edərək, bir dəyişənə müxtəlif tiplərdə qiymətlər təyin edirik. `any` əslində `interface{}`-nin sinonimidir və Go 1.18 ilə daha oxunaqlı şəkildə yazmaq üçün tətbiq olunmuşdur.

### Məlumatın Tipinin Yoxlanılması

`any` tipi ilə işləyərkən, `interface{}` ilə olduğu kimi, məlumatın tipini yoxlamaq üçün **type assertion** (tip təsdiqi) və **type switch** (tip çevirmə) üsullarından istifadə edə bilərik.

```go
package main

import "fmt"

func main() {
    var dynamicVar any = "Hello, Go!"

    // Type assertion with `any`
    str, ok := dynamicVar.(string)
    if ok {
        fmt.Println("String value:", str)
    } else {
        fmt.Println("Not a string")
    }

    // Type switch with `any`
    switch v := dynamicVar.(type) {
    case string:
        fmt.Println("String value:", v)
    case int:
        fmt.Println("Integer value:", v)
    default:
        fmt.Println("Unknown type")
    }
}
```

Bu nümunədə, `any` tipi ilə təyin edilmiş bir dəyişənin tipi yoxlanılır və uyğun əməliyyatlar həyata keçirilir.

## Nəticə

Golang-da dinamik dəyişənlər birbaşa mövcud olmasa da, `interface{}` və onun sinonimi olan `any` tipləri vasitəsilə dinamik tiplərlə işləmək mümkündür. Bu tiplər, dəyişənlərə müxtəlif məlumat tipləri təyin etməyə və daha çevik proqramlar yazmağa imkan verir. `any` tipi ilə Go dilində kod yazmaq daha oxunaqlı olur və Go 1.18-dən etibarən daha geniş istifadə olunur.


# Generics

**Generics**, Go 1.18 versiyasından etibarən dilə daxil edilmiş çox güclü bir xüsusiyyətdir. Generics, müxtəlif məlumat tipləri ilə işləyən funksiyalar və strukturlar yazmağa imkan verir, bu isə kodun yenidən istifadə olunabilirliyini artırır. Generics olmadan, biz adətən eyni funksiyanı müxtəlif tiplərə uyğun olaraq çoxaldırdıq, lakin generics sayəsində bu ehtiyac aradan qalxır.

## Generics-in Əsas Məntiqi

Generics bizə bir `function`ın və ya `struct`ın tip parametrlərindən asılı olmadan işləməsinə imkan verir. Go-da generics istifadə edərkən, biz **type parameters** (tip parametrləri) təyin edirik. Bu parametrlər `[]` kvadrat mötərizələrin içərisində müəyyən edilir və həmin tip parametrlər `function` və ya `struct` daxilində istifadə edilə bilər.

## Generics ilə Function Nümunəsi

Tutaq ki, biz iki elementi müqayisə edən bir funksiya yazmaq istəyirik. Bu funksiya həm integer, həm float, həm də string tiplərində işləyə bilməlidir.

```go
package main

import "fmt"

func Compare[T comparable](a, b T) bool {
    return a == b
}

func main() {
    fmt.Println(Compare(10, 10))       // True, both are int
    fmt.Println(Compare("Go", "Go"))   // True, both are string
    fmt.Println(Compare(3.14, 2.71))   // False, both are float64
}
```

### Bu Nümunədə:

* `Compare` funksiyası generik bir funksiyadır və `T` tip parametri ilə işləyir.
* `T` tipi `comparable` məhdudiyyəti ilə təyin olunmuşdur. Bu məhdudiyyət, `T` tipinin müqayisə edilə bilən (`==` və `!=` operatorları ilə) olmasını tələb edir.
* Eyni funksiyadan fərqli tiplərlə istifadə edə bilərik, məsələn, `int`, `string`, `float64` və s.

## Generics ilə Struct Nümunəsi

Generics sadəcə funksiyalarda deyil, həmçinin strukturlarda da istifadə edilə bilər. Bu, strukturların içərisində müxtəlif tiplərlə işləməyi mümkün edir.

```go
package main

import "fmt"

type Box[T any] struct {
    value T
}

func (b Box[T]) GetValue() T {
    return b.value
}

func main() {
    intBox := Box[int]{value: 100}
    fmt.Println("Integer Box:", intBox.GetValue())

    strBox := Box[string]{value: "Hello"}
    fmt.Println("String Box:", strBox.GetValue())
}
```

### Bu Nümunədə:

* `Box` adlı bir generik struktur təyin edirik. `T` tip parametri struktur daxilindəki `value` sahəsinin tipini təyin edir.
* `GetValue` metodu `T` tipini qaytarır, yəni bu metod dinamik olaraq fərqli tipləri geri qaytara bilər.
* Biz `Box` strukturundan həm integer, həm də string tipləri ilə istifadə edirik.

## Generics ilə Məhdudiyyətlər (Constraints)

Generics-də tiplər üçün məhdudiyyətlər təyin edə bilərik. Məhdudiyyətlər vasitəsilə, bir generik funksiyanın və ya strukturun hansı tip parametrləri qəbul edəcəyini idarə edə bilərik. Məsələn, `comparable`, `any`, və ya öz xüsusi interfeyslərimizdən istifadə edə bilərik.

```go
package main

import "fmt"

// Generic function with constraint
func Add[T int | float64](a, b T) T {
    return a + b
}

func main() {
    fmt.Println(Add(5, 10))       // Integer addition
    fmt.Println(Add(3.14, 2.71))  // Float addition
}
```

### Bu Nümunədə:

* `Add` funksiyası yalnız `int` və `float64` tiplərində işləyə bilər, çünki biz məhdudiyyət kimi `T int | float64` təyin etmişik.
* Funksiya iki parametri əlavə edir və onların tipinə uyğun olaraq qaytarır.

## Nəticə

Generics Go dilində kodu daha çevik və yenidən istifadə edilə bilən edir. Funksiyalar və strukturlar bir neçə məlumat tipi ilə işləyə bilər və generics ilə yazılan kodun daha az çoxaldılması ehtiyacı olur. Generics-dən istifadə edərkən tip parametrlərini və məhdudiyyətləri düzgün təyin etməklə kodun daha oxunaqlı və stabil olmasını təmin edə bilərik.


# Struct embedding

Go dilində struct embedding, bir struct-ın başqa bir struct daxilində yerləşdirilməsi ilə digər struct-ın sahələrinə və metodlarına birbaşa giriş imkanı verir. Bu, təkcə kod təkrarını azaltmır, həm də kompozisiyanı təşviq edir, beləliklə, bir struct-ın başqa bir struct-a miras buraxmadan onun funksionallığını təmin etməyə imkan verir.

```go
package main

import "fmt"

type Person struct {
	Name string
	Age  int
}

func (p Person) SayHello() {
	fmt.Printf("Hello, my name is %s and I'm %d years old\n", p.Name, p.Age)
}

type Employee struct {
	Person
	Company string
}

func main() {
	emp := Employee{
		Person: Person{
			Name: "John",
			Age:  30,
		},
		Company: "ABC Inc.",
	}

	emp.SayHello()
}
```

Bu nümunədə, Person adlı bir struct təyin edilir və Name və Age adlı iki sahəsi var. Eyni zamanda SayHello adlı bir funksiyası mövcuddur.

Employee adlı bir struct təyin edilir və onun içərisində Person struct-ı yerləşdirilir. Employee-nin əlavə olaraq Company adlı bir sahəsi də var. main funksiyasında, emp adlı bir Employee dəyişəni yaradılır. Bu dəyişənin Person sahəsinə Person tipində bir dəyər atanır. SayHello funksiyası emp dəyişəni üzərindən çağırılır və nəticə ekrana yazdırılır.

Output:

```go
Hello, my name is John and I'm 30 years old
```

Bu nümunədə, Employee adlı struktur, Person strukturunu daxili olaraq yerləşdirir. Bu, Employee strukturuna Person-un bütün sahələrinə və funksiyalarına birbaşa çıxış imkanı verir. Beləliklə, Employee strukturunu istifadə edərək həm Employee, həm də Person məlumatlarına asanlıqla giriş əldə etmək mümkündür. Bu xüsusiyyət, kod təkrarını azaldır və strukturları daha modul halına gətirir.


# Errors

Go dilində səhv (error) idarəetməsi, error adlı bir məlumat tipi vasitəsilə həyata keçirilir. Bu tip, bir funksiyanın nəticəsi olaraq ya bir səhv mesajı, ya da nil dəyərini qaytarmaq üçün istifadə olunur. Bu, proqramın gözlənilməyən vəziyyətlərdə necə davranmalı olduğunu idarə etməyə kömək edir.

```go
package main

import (
	"errors"
	"fmt"
)

func divide(x, y float64) (float64, error) {
	if y == 0 {
		return 0, errors.New("Cannot divide by zero")
	}
	return x / y, nil
}

func main() {
	result, err := divide(10, 2)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(result)
	}

	result, err = divide(10, 0)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(result)
	}
}
```

Bu nümunədə, divide adlı bir funksiya təyin edilir və iki ədədi bölür. Lakin, əgər ikinci ədəd 0 olarsa, bir səhv qaytarır.

main funksiyasında, divide funksiyası iki fərqli parametr ilə çağırılır. err adlı bir səhv dəyişəni istifadə edilərək hər bir çağırışın nəticəsi yoxlanılır. Əgər səhv varsa, səhv mesajı ekrana yazdırılır. Əks halda, nəticə ekrana yazdırılır.

Output:

```go
5 <nil>
Cannot divide by zero
```

Bu nümunədə divide funksiyası sıfıra bölmə vəziyyətini idarə etmək üçün səhv idarəetməsindən istifadə edir. Əgər y sıfır olarsa, errors.New ilə yaradılmış səhv qaytarılır. Proqram err dəyişəni ilə səhv olub-olmadığını yoxlayır və əgər səhv varsa, onu ekrana yazdırır. Bu yanaşma Go dilində səhv idarəetməsini daha etibarlı və idarə olunan edir.

\ <br>


# Goroutines

Go dilində goroutine-lər, eyni anda çalışan əməliyyatlardır. Goroutine-lər go açar sözü istifadə edilərək yaradılır və fərqli əməliyyatları eyni vaxtda həyata keçirmək üçün istifadə olunur.

```go
package main

import (
	"fmt"
	"time"
)

func sayHello() {
	fmt.Println("Hello")
}

func main() {
	go sayHello() // goroutine
	time.Sleep(time.Second)
	fmt.Println("World")
}
```

Bu nümunədə, sayHello adlı bir funksiya təyin edilir və "Hello" mesajını ekrana yazdırır.

main funksiyasında, sayHello funksiyası bir goroutine olaraq çağırılır. Bu səbəbdən, sayHello funksiyasının icrası digər əməliyyatlardan müstəqil olaraq baş verir. time.Sleep funksiyası bir saniyəlik gözləmə müddəti əlavə edir. Nəticədə, "World" mesajı ekrana yazdırılır.

Output:

```go
Hello
World
```

Bu nümunədə, goroutine istifadə edərək sayHello funksiyası eyni anda çalışdırıldı. sayHello funksiyası goroutine olaraq çağırıldığı üçün digər əməliyyatlardan müstəqil işləd və nəticədə ekrana "Hello" mesajı yazdırıldıktan sonra "World" mesajı yazdırıldı.

\ <br>


# Channel

Go dilində kanal (channel), goroutine-lər arasında məlumat ötürmək üçün istifadə olunan bir məlumat strukturudur. Kanal, make açar sözü ilə yaradılır və <- operatoru ilə məlumat göndərmə və qəbul etmə əməliyyatları həyata keçirilir.

```go
package main

import "fmt"

func main() {
	messages := make(chan string)

	go func() { messages <- "Hello" }()

	msg := <-messages
	fmt.Println(msg)
}
```

Bu nümunədə, messages adlı bir kanal yaradılır.

go açar sözü ilə bir goroutine yaradılır və bu goroutine messages kanalına "Hello" mesajını göndərir.

main funksiyasında, msg adlı bir dəyişkənə messages kanalından bir mesaj alınır və ekrana yazdırılır.

Output:

```go
Hello
```

Bu örnekte, channel kullanarak, bir goroutine'dan ana iş parçacığına bir mesaj gönderildi. Bu nedenle, goroutine işlemi tamamlandıktan sonra ana iş parçacığı channel'dan mesajı alır ve sonucu ekrana yazdırır.

Kanallar, Golang'de birçok durumda kullanılabilir, örneğin:

Bu nümunədə, kanal istifadə edərək bir goroutine-dən ana iş parçacığına bir mesaj göndərildi. Bu səbəbdən, goroutine tamamlandıqdan sonra ana iş parçacığı kanaldan mesajı alır və nəticəni ekrana yazdırır.

Kanallar Go dilində bir çox vəziyyətdə istifadə oluna bilər, məsələn:

1. Goroutine-lər arasında məlumat mübadiləsi üçün
2. Sinxronizasiya əməliyyatları üçün
3. Tətbiqinizin performansını artırmaq üçün (paralel emal etmək)
4. Goroutine-lər arasında məlumat yarışlarını qarşısını almaq üçün
5. Tapşırıqların koordinasiyası və sinxronizasiyası üçün

\ <br>


# Select

Go dilində, select açar sözü bir neçə kanalı (channel) dinləyərək hansı kanalın mesaj göndərdiyini müəyyənləşdirə bilər. Bu xüsusiyyət kanalların sinxronizasiyasını asanlaşdırır və fərqli goroutin-lər arasında mesajlaşmanı idarə edir.

```go
package main

import (
	"fmt"
	"time"
)

func main() {
	c1 := make(chan string)
	c2 := make(chan string)

	go func() {
		time.Sleep(time.Second * 1)
		c1 <- "one"
	}()

	go func() {
		time.Sleep(time.Second * 2)
		c2 <- "two"
	}()

	for i := 0; i < 2; i++ {
		select {
		case msg1 := <-c1:
			fmt.Println("received", msg1)
		case msg2 := <-c2:
			fmt.Println("received", msg2)
		}
	}
}
```

Bu nümunədə, c1 və c2 adlı iki kanal yaradılır və mesaj göndərmə prosesi üçün goroutin-lər yaradılır.

main funksiyasında, select açar sözü istifadə edilərək, c1 və c2 kanalları dinlənilir. İlk olaraq, goroutin-lər arasındakı gözləmə müddətinə görə, c1 kanalından bir mesaj alınır və ekrana yazdırılır. Daha sonra, c2 kanalından bir mesaj alınır və ekrana yazdırılır.

Output:

```go
received one
received two
```

Bu nümunədə, select açar sözü istifadə edilərək, c1 və c2 kanallarını dinləyən bir for döngüsü yaradıldı. Bu, mesaj alım müddətinə əsaslanaraq fərqli kanalların dinlənilməsinə imkan verir. Nəticədə, goroutin-lər arasındakı mesajlaşma müəyyən bir qaydada həyata keçirilir və select açar sözü istifadə edilərək sinxronizasiya təmin edilir.


# Timeouts

Go dilində, **timeout** əməliyyatları xüsusilə şəbəkə əməliyyatları zamanı əhəmiyyətlidir. `time` paketi istifadə edilərək, müəyyən bir müddət gözləmək təmin edilə bilər. Əgər bu müddət keçərsə, timeout səhvi yaranır.

```go
package main

import (
	"fmt"
	"time"
)

func main() {
	c1 := make(chan string, 1)

	go func() {
		time.Sleep(time.Second * 2)
		c1 <- "result 1"
	}()

	select {
	case res := <-c1:
		fmt.Println(res)
	case <-time.After(time.Second * 1):
		fmt.Println("timeout 1")
	}

	c2 := make(chan string, 1)

	go func() {
		time.Sleep(time.Second * 2)
		c2 <- "result 2"
	}()

	select {
	case res := <-c2:
		fmt.Println(res)
	case <-time.After(time.Second * 3):
		fmt.Println("timeout 2")
	}
}
```

Bu nümunədə, `c1` və `c2` adlı iki kanal yaradılır və mesaj göndərmə əməliyyatları üçün goroutin-lər yaradılır.

İlk olaraq, `select` açar sözü istifadə edilərək, `c1` kanalından mesaj gözlənilir. Amma `time.After` istifadə edilərək, `c1` kanalından bir mesaj alınmazdan əvvəl 1 saniyə gözlənilir. Əgər 1 saniyədən çox müddət keçərsə, timeout səhvi yaranır və "timeout 1" mesajı ekrana yazdırılır.

Daha sonra, `select` açar sözü ilə `c2` kanalından mesaj gözlənilir. Bu dəfə `time.After` ilə 3 saniyə gözlənilir və nəticə mesajı alınır.

#### Output:

```go
timeout 1
result 2
```

Bu nümunədə `time.After` istifadə edərək timeout əməliyyatları icra edilir. İlk nümunədə, `c1` kanalına 1 saniyədən əvvəl mesaj göndərilmədiyi üçün timeout səhvi yaranır. İkinci nümunədə isə, `c2` kanalına mesaj vaxtında göndərildiyi üçün mesaj qəbul edilir və ekrana yazdırılır.


# Non-Blocking Channel Operations

Go dilində, channel-lar adətən bloklayan xüsusiyyətə malikdir, yəni bir goroutine bir channel-a mesaj göndərmək və ya almaq istədikdə həmin əməliyyat tamamlanana qədər gözləyir. Amma `select` açar sözü ilə channel-ların bloklanmadan işləməsi təmin edilə bilər.

```go
package main

import "fmt"

func main() {
	messages := make(chan string)
	signals := make(chan bool)

	select {
	case msg := <-messages:
		fmt.Println("received message", msg)
	default:
		fmt.Println("no message received")
	}

	msg := "hello"
	select {
	case messages <- msg:
		fmt.Println("sent message", msg)
	default:
		fmt.Println("no message sent")
	}

	select {
	case msg := <-messages:
		fmt.Println("received message", msg)
	default:
		fmt.Println("no message received")
	}

	select {
	case sig := <-signals:
		fmt.Println("received signal", sig)
	default:
		fmt.Println("no signal received")
	}

	signal := true
	select {
	case signals <- signal:
		fmt.Println("sent signal", signal)
	default:
		fmt.Println("no signal sent")
	}

	select {
	case sig := <-signals:
		fmt.Println("received signal", sig)
	default:
		fmt.Println("no signal received")
	}
}
```

Bu nümunədə iki channel (`messages` və `signals`) yaradılır və onlardan bloklanmadan necə məlumat göndərmək və qəbul etmək göstərilir.

#### Output:

```go
no message received
sent message hello
no message received
no signal received
sent signal true
received signal true
```

Bu nümunədə:

* İlk öncə `messages` channel-dan mesaj alınmazdan əvvəl `no message received` mesajı göstərilir.
* Daha sonra `hello` mesajı `messages` channel-ına göndərilir və "sent message hello" yazısı ekrana çıxır.
* İkinci dəfə `messages` channel-dan mesaj almağa çalışılsa da, yeni mesaj olmadığından "no message received" göstərilir.
* Eyni üsulla `signals` channel-ı üçün sinyal göndərilməzdən əvvəl "no signal received" göstərilir, sinyal göndəriləndən sonra isə qəbul edildiyi göstərilir.

Bloklamadan əməliyyatlar `select` ilə kanal vasitəsilə sinxronlaşma və qarşılıqlı əlaqənin daha çevik formada həyata keçirilməsinə imkan yaradır.


# Closing Channels

Go dilində, channel-ların bağlanması, məlumatların göndərilməsi və alınması prosesində sinxronizasiyanı təmin edir. `close` funksiyası vasitəsilə channel bağlanır. Bağlanmış bir channel-a mesaj göndərmək mümkün deyil və həmin channel-dan daha çox mesaj alınmaz.

```go
package main

import "fmt"

func main() {
	jobs := make(chan int, 5)
	done := make(chan bool)

	go func() {
		for {
			j, more := <-jobs
			if more {
				fmt.Println("received job", j)
			} else {
				fmt.Println("received all jobs")
				done <- true
				return
			}
		}
	}()

	for j := 1; j <= 3; j++ {
		jobs <- j
		fmt.Println("sent job", j)
	}

	close(jobs)
	fmt.Println("sent all jobs")

	<-done
}
```

Bu nümunədə:

* `jobs` adlı bir channel yaradılır və işlər bu kanala göndərilir.
* `done` adlı bir channel yaradılır və işlərin tamamlandığını bildirmək üçün istifadə olunur.
* Bir `goroutine` yaradılır və bu `goroutine`, `jobs` channel-ından mesajları alır. Alınan mesajların sayı azaldıqca hər dəfə "received job" yazısı çap edilir.
* `for` döngüsü ilə `jobs` channel-ına 3 iş göndərilir və hər göndərmə zamanı "sent job" mesajı göstərilir.
* `jobs` channel-ı bağlandıqdan sonra `goroutine`, channel-dan bütün mesajları alır və son olaraq "received all jobs" mesajı göstərilir.

#### Output:

```go
sent job 1
sent job 2
sent job 3
sent all jobs
received job 1
received job 2
received job 3
received all jobs
```

Bu nümunə, Go dilində bir channel-a mesaj göndərdikdən sonra onu necə bağlayacağınızı və bağlanmış channel-dan daha çox mesaj alınmadığını göstərir. Bağlı bir channel-dan oxumağa davam edə bilərsiniz, lakin artıq yeni mesajlar göndərilə bilməz.


# Range over Channels

Go dilində, `range` açar sözü channel-lar üzərində dövr etməyə imkan verir. Bu üsul ilə channel bağlanana qədər bütün mesajlar `for` dövrü ilə oxuna bilər.

```go
package main

import "fmt"

func main() {
	queue := make(chan string, 2)
	queue <- "one"
	queue <- "two"
	close(queue)

	for elem := range queue {
		fmt.Println(elem)
	}
}
```

Bu nümunədə:

* `queue` adlı bir channel yaradılır və bu channel-ə iki mesaj göndərilir.
* `close` funksiyası ilə channel bağlanır.
* Daha sonra, `range` açar sözü ilə for dövrü istifadə edərək, channel-dakı bütün mesajlar oxunur.

#### Output:

```go
one
two
```

Bu nümunədə `range` açar sözü ilə channel-dakı bütün mesajlar oxundu və `close` funksiyası ilə channel bağlandıqdan sonra belə, göndərilmiş bütün mesajlar alındı. `range` dövrü channel bağlanana qədər davam edir və channel-dan yeni mesajlar gəldikcə onları oxuyur.


# Timers

Go dilində timer-lər, müəyyən bir müddət keçdikdən sonra bir əməliyyatın yerinə yetirilməsini təmin etmək üçün istifadə olunur. `time` paketi daxilindəki `NewTimer` funksiyası ilə timer yaradıla bilər.

```go
package main

import (
	"fmt"
	"time"
)

func main() {
	timer1 := time.NewTimer(2 * time.Second)

	<-timer1.C
	fmt.Println("Timer 1 expired")

	timer2 := time.NewTimer(time.Second)

	go func() {
		<-timer2.C
		fmt.Println("Timer 2 expired")
	}()

	stop2 := timer2.Stop()
	if stop2 {
		fmt.Println("Timer 2 stopped")
	}
}
```

Bu nümunədə:

* `NewTimer` funksiyası ilə iki timer yaradılır.
* Birinci timer (`timer1`) 2 saniyədən sonra bitəcək şəkildə təyin olunur. Timer bitdikdə `<-timer1.C` ilə gözlənilir və "Timer 1 expired" mesajı ekrana yazdırılır.
* İkinci timer (`timer2`) 1 saniyədən sonra sona çatacaq. `goroutine` ilə bu timer izlənir və əgər timer vaxtı bitərsə, "Timer 2 expired" mesajı çıxar. Lakin `Stop` funksiyası ilə bu timer vaxtı dolmadan dayandırılır və "Timer 2 stopped" mesajı ekrana yazdırılır.

#### Output:

```go
Timer 1 expired
Timer 2 stopped
```

Bu nümunədə, timer-lərdən biri müəyyən müddətdən sonra bitir və bir əməliyyat yerinə yetirilir, digəri isə vaxtı dolmadan əvvəl `Stop` funksiyası ilə dayandırılır. Timer-lərin bu cür idarə olunması zamanlama əməliyyatlarının nəzarətində faydalıdır.


# Ticker

Go dilində, ticker'lər müəyyən bir müddət ərzində müəyyən aralıqlarla bir əməliyyatı yerinə yetirmək üçün istifadə olunur. `time` paketi daxilində yer alan `NewTicker` funksiyası istifadə edilərək, bir ticker yaradılır.

```go
package main

import (
	"fmt"
	"time"
)

func main() {
	ticker := time.NewTicker(500 * time.Millisecond)
	done := make(chan bool)

	go func() {
		for {
			select {
			case <-done:
				return
			case t := <-ticker.C:
				fmt.Println("Tick at", t)
			}
		}
	}()

	time.Sleep(1600 * time.Millisecond)
	ticker.Stop()
	done <- true
	fmt.Println("Ticker stopped")
}
```

Bu nümunədə, `NewTicker` funksiyası istifadə edilərək, bir ticker yaradılır və hər 500 millisekundda bir əməliyyat yerinə yetirilir.

`goroutine` istifadə edilərək, `select` açar sözü ilə, ticker'ın müəyyən edilmiş zaman aralığına uyğun olaraq müəyyən aralıqlarla əməliyyat həyata keçirilir. `done` adlı kanal vasitəsilə mesaj göndərilərək, `goroutine` dayandırılır və "Ticker stopped" mesajı ekrana yazdırılır.

Output:

```go
Tick at 2022-11-27 16:37:14.750915 +0300 MSK m=+0.501356162
Tick at 2022-11-27 16:37:15.249568 +0300 MSK m=+1.000018689
Tick at 2022-11-27 16:37:15.751064 +0300 MSK m=+1.501505306
Ticker stopped
```


# Worker pools

Go dilində, worker pool'lar müəyyən bir işi yerinə yetirmək üçün təyin olunmuş işçi qrupudur. İşlər `channel`-a göndərilir və bu işlər işçilər tərəfindən emal olunur. Bu metod, paralel emal və işlərin işçilər arasında paylanması ilə iş yükünü balanslaşdırmağa kömək edir.

```go
package main

import (
	"fmt"
	"time"
)

func worker(id int, jobs <-chan int, results chan<- int) {
	for j := range jobs {
		fmt.Println("Worker", id, "started job", j)
		time.Sleep(time.Second)
		fmt.Println("Worker", id, "finished job", j)
		results <- j * 2
	}
}

func main() {
	jobs := make(chan int, 100)
	results := make(chan int, 100)

	for w := 1; w <= 3; w++ {
		go worker(w, jobs, results)
	}

	for j := 1; j <= 5; j++ {
		jobs <- j
	}
	close(jobs)

	for a := 1; a <= 5; a++ {
		<-results
	}
}
```

Bu nümunədə, `worker` adlı bir funksiya yaradılır. Bu funksiya işçilər tərəfindən emal olunacaq işləri alır və nəticələri `results` kanalına geri qaytarır.

* `jobs` adlı bir kanal yaradılır və bu kanal vasitəsilə 100-ə qədər iş yükləri göndərilə bilər.
* `results` adlı başqa bir kanal yaradılır və 100 nəticə bu kanal vasitəsilə geri alına bilər.

İlk `for` dövrü ilə, 3 işçi üçün `goroutine` yaradılır. Hər işçi `worker` funksiyasını icra edərək, ona verilmiş işləri emal edir.

Növbəti `for` dövrü ilə, 5 iş göndərilir və bu işlər `jobs` kanalına daxil edilir. `close` funksiyası vasitəsilə `jobs` kanalı bağlanır.

Sonuncu `for` dövrü ilə, emal olunmuş nəticələr `results` kanalından alınır.

**Output:**

```go
Worker 3 started job 1
Worker 2 started job 2
Worker 1 started job 3
Worker 3 finished job 1
Worker 3 started job 4
Worker 2 finished job 2
Worker 1 finished job 3
Worker 1 started job 5
Worker 3 finished job 4
Worker 1 finished job 5
```

Bu nümunə, işlərin işçilər arasında necə paylanmasını və nəticələrin necə geri alındığını göstərir.


# WaitGroups

`WaitGroup` strukturu, Go dilində goroutine-lərin sinxronizasiyası üçün istifadə olunan bir mexanizmdir. Go-nun `sync` paketində tapılan bu struktur, birdən çox goroutine-in icrasını izləmək və onların hamısı tamamlandıqda müəyyən bir əməliyyatı yerinə yetirmək üçün imkan yaradır.

`WaitGroup`, birdən çox goroutine-i izləməyə, onların bitməsini gözləməyə və hər bir goroutine tamamlandıqda xəbərdar olmağa imkan verir. Bu məqsədlə, `Add()`, `Done()` və `Wait()` metodlarından istifadə edilir:

* `Add(n)`: `n` sayda goroutine əlavə edir.
* `Done()`: Goroutine bitdikdən sonra çağırılır və gözləmə sayını azaldır.
* `Wait()`: Bütün goroutine-lər tamamlanana qədər gözləyir.

Aşağıdakı nümunədə, `WaitGroup` istifadə edərək goroutine-lərin idarə olunması göstərilir:

```go
package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()  // Goroutine tamamlandıqda Done() çağırılır.

    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)  // İşçinin işləməsi üçün bir saniyə gözləmə.
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup  // WaitGroup dəyişəni yaradılır.

    for i := 1; i <= 5; i++ {
        wg.Add(1)  // Hər goroutine üçün Add(1) edilir.
        go worker(i, &wg)  // Goroutine işə salınır.
    }

    wg.Wait()  // Bütün goroutine-lər bitənə qədər gözləyir.

    fmt.Println("All workers done")
}
```

**Nümunə açıqlaması:**

* `worker` funksiyası bir işçini təmsil edir. Hər işçi bir saniyə işləyir və tamamlandıqda `wg.Done()` çağırılır.
* `main` funksiyasında, 5 goroutine işə salınır və hər biri `worker` funksiyasını icra edir.
* `wg.Add(1)` hər işçi başlamazdan əvvəl sayğacı bir vahid artırır.
* `wg.Wait()` bütün goroutine-lər bitmədən `main` funksiyasının bitməsini gecikdirir.

&#x20;**Output:**

```go
Worker 5 starting
Worker 3 starting
Worker 4 starting
Worker 1 starting
Worker 2 starting
Worker 5 done
Worker 3 done
Worker 2 done
Worker 1 done
Worker 4 done
All workers done
```

Bu nümunədə, 5 goroutine paralel olaraq işləyir, hər biri tamamlandıqda `WaitGroup` onları izləyir və bütün işlər tamamlandıqda proqram "All workers done" mesajını yazdırır.


# Rate Limiting

Rate Limiting, bir API və ya xidmətin istifadəçinin müəyyən bir zaman ərzində edə biləcəyi sorğu sayını məhdudlaşdırmaq üçün istifadə edilən bir metoddur. Bu metod, tətbiqin və ya xidmətin həddindən artıq yüklənməsinin qarşısını almaq üçün istifadə oluna bilər.

Go dilində, Rate Limiting etmək üçün `time` paketi istifadə oluna bilər. Bu paket, müəyyən bir zaman çərçivəsində müəyyən bir əməliyyatın yerinə yetirilməsi üçün nə qədər gözləmək lazım olduğunu hesablamaq üçün istifadə edilir.

Aşağıdakı nümunədə, `time.Ticker` və `time.Sleep` istifadə edilərək Rate Limiting nümunəsi göstərilir:

```golang
package main

import (
    "fmt"
    "time"
)

func main() {
    requests := make(chan int, 5)

    for i := 1; i <= 5; i++ {
        requests <- i
    }
    close(requests)

    limiter := time.Tick(200 * time.Millisecond)

    for req := range requests {
        <-limiter
        fmt.Println("request", req, time.Now())
    }
}
```

Bu nümunədə, `requests` adlı bir kanal yaradılır və içinə 5 sorğu əlavə edilir. Daha sonra `limiter` adlı bir `time.Ticker` yaradılır və 200 millisaniyəlik bir müddətlə məhdudlaşdırılır.

Daha sonra, `requests` kanalındakı hər bir sorğu məhdudlaşdırıcıya uyğun olaraq işlədir. Hər sorğu arasında 200 millisaniyəlik fasilə ilə işlənməsi təmin edilir.

```
request 1 2023-05-23 15:56:01.46705 +0300 +03 m=+0.201167209
request 2 2023-05-23 15:56:01.667008 +0300 +03 m=+0.401132584
request 3 2023-05-23 15:56:01.867014 +0300 +03 m=+0.601146918
request 4 2023-05-23 15:56:02.067052 +0300 +03 m=+0.801193043
request 5 2023-05-23 15:56:02.267025 +0300 +03 m=+1.001173793
```

Bu şəkildə, Rate Limiting istifadə edərək sorğular müəyyən bir sürətlə işlənir.


# Atomic Counter

Atomic Counters mövzusu, Go dilində paralelliyi idarə etmək üçün istifadə olunan bir mövzudur. Çoxsaylı Go proqramları bir neçə goroutine tərəfindən paylaşılan verilərlə işlədiyinə görə, bu verilərin eyni anda bir neçə goroutine tərəfindən dəyişdirilməsi labüddür. Bu dəyişikliklər düzgün idarə edilmədikdə, proqram gözlənilməz şəkildə işləyə bilər.

Go dilində Atomic Counter-lər `sync/atomic` paketində mövcuddur. Bu paketdə `AddInt64`, `CompareAndSwapInt64`, `SwapInt64`, `LoadInt64`, və `StoreInt64` kimi metodlar mövcuddur. Bu metodlar, bir neçə goroutine tərəfindən eyni anda daxil olunan dəyişkənin təhlükəsiz şəkildə dəyişdirilməsini təmin edir.

Aşağıdakı nümunə, Atomic Counter istifadə edərək, 10 goroutine tərəfindən paylaşılan bir sayaç dəyişkəninin təhlükəsiz şəkildə artırılmasını göstərir:

```golang
package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

func main() {
    var counter int64 = 0
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            atomic.AddInt64(&counter, 1)
        }()
    }
    wg.Wait()
    fmt.Println("Counter:", counter)
}
```

Bu nümunədə, `counter` adlı dəyişkən `int64` tipində bir Atomic Counter olaraq təyin olunur və başlanğıcda 0 dəyəri ilə müəyyən edilir. Daha sonra, 10 goroutine yaradılır və hər biri `atomic.AddInt64` metodundan istifadə edərək `counter` dəyişkənini artırır. Bu metod `&counter` ünvanını alır və `counter` dəyişkəninə atomik şəkildə 1 əlavə edir.

Nəticədə, Atomic Counter veri quruluşu, çoxsaylı goroutine-lər tərəfindən paylaşılan dəyişkənlərin təhlükəsiz şəkildə dəyişdirilməsini təmin edir və paralellik idarəsində mühüm rol oynayır.


# Sorting

Golang-də sıralama əməliyyatı üçün `sort` paketi istifadə olunur. Bu paket daxilində müxtəlif məlumat tiplərinə aid sıralama əməliyyatları aparmaq üçün funksiyalar mövcuddur.

Ən çox istifadə olunan iki sıralama funksiyası `sort.Ints()` və `sort.Strings()` funksiyalarıdır. `sort.Ints()` funksiyası `int` tipində slice-i kiçikdən böyüyə doğru sıralayır. `sort.Strings()` funksiyası isə `string` tipində slice-i əlifba sırası ilə sıralayır.

Bundan əlavə, `sort.Float64s()` funksiyası da mövcuddur və `float64` tipində slice-i kiçikdən böyüyə doğru sıralayır.

Bundan başqa, `sort.Slice()` funksiyası ilə xüsusi sıralama əməliyyatları da həyata keçirilə bilər. Bu üsul, bir slice və bir `Less` funksiyası alır. `Less` funksiyası iki slice elementini müqayisə etmək üçün istifadə olunur və nəticəsinə əsasən elementlərin sıralanmasına qərar verilir. Bu funksiya ilə slice elementlərinə görə xüsusi sıralama əməliyyatları həyata keçirilə bilər.

Məsələn, aşağıdakı nümunədə `sort.Slice()` funksiyası istifadə edilərək xüsusi bir sıralama əməliyyatı həyata keçirilir:

```go
package main

import (
	"fmt"
	"sort"
)

type Person struct {
	Name string
	Age  int
}

func main() {
	people := []Person{
		{"Alice", 25},
		{"Bob", 30},
		{"Charlie", 20},
	}

	sort.Slice(people, func(i, j int) bool {
		return people[i].Age < people[j].Age
	})

	fmt.Println(people)
}
```

Bu nümunədə, `Person` tipində bir slice təyin edilir və `sort.Slice()` funksiyası istifadə edilərək `Age` sahəsinə görə kiçikdən böyüyə doğru sıralama aparılır.

Output:

```
[{Charlie 20} {Alice 25} {Bob 30}]
```

İlk nümunədə, `sort.Ints()` funksiyası istifadə edilərək `ints` adlı bir slice kiçikdən böyüyə doğru sıralanır və nəticə ekrana yazdırılır:

```go
package main

import (
	"fmt"
	"sort"
)

func main() {
	ints := []int{5, 2, 7, 1, 9, 3, 8, 6, 4}
	sort.Ints(ints)
	fmt.Println(ints)

	if sort.IntsAreSorted(ints) {
		fmt.Println("Slice is sorted.")
	} else {
		fmt.Println("Slice is not sorted.")
	}
}
```

Output:

```go
[1 2 3 4 5 6 7 8 9]
Slice is sorted.
```

İkinci nümunədə isə `sort.Strings()` funksiyası istifadə edilərək `strings` adlı bir slice əlifba sırasına görə sıralanır və nəticə ekrana yazdırılır:

```go
package main

import (
	"fmt"
	"sort"
)

func main() {
	strings := []string{"apple", "banana", "cherry", "date", "elderberry", "fig"}
	sort.Strings(strings)
	fmt.Println(strings)

	if sort.StringsAreSorted(strings) {
		fmt.Println("Slice is sorted.")
	} else {
		fmt.Println("Slice is not sorted.")
	}
}
```

Output:

```go
[apple banana cherry date elderberry fig]
Slice is sorted.
```


# Panic

`panic`, Go dilində bir proqram işləmə zamanı gözlənilməz bir vəziyyətlə qarşılaşdıqda, proqramın icrasını dayandıraraq bir xəta mesajı göstərməsini təmin edən xüsusiyyətdir.

`panic` ciddi səhvləri idarə etmək üçün istifadə edilməlidir və mümkün qədər qarşısını almağa çalışılmalıdır. Lakin, kritik bir xəta baş verdikdə, proqramın dərhal dayandırılması lazım olan vəziyyətlərdə `panic` faydalı ola bilər.

### `panic` istifadə nümunəsi:

```golang
package main

import "fmt"

func main() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("Panic yakalandı:", err)
		}
	}()

	divideByZero()
	fmt.Println("Bu kod çalışacak mı?")
}

func divideByZero() {
	numerator := 10
	denominator := 0
	result := numerator / denominator
	fmt.Println("Sonuç:", result)
}
```

Bu nümunədə, `divideByZero` adlı bir funksiya var. Bu funksiya daxilində `numerator` 10 və `denominator` 0 olaraq təyin edilir, daha sonra `result` dəyişəninə `numerator`u `denominator`ə bölürük. Lakin, `denominator`un 0 olması vəziyyətində, sıfıra bölmə xətası baş verir və bu, `panic` vəziyyətini yaradır.

### **Açıqlama**:

1. **Panic və recover istifadəçi:**\
   `defer` istifadə edərək bir funksiyanı gecikdiririk, burada `recover()` funksiyası `panic` vəziyyətlərini ələ keçirir. Əgər `panic` vəziyyəti yaranarsa, `recover()` həmin `panic` səbəbini tapır və işləyir.
2. **Sıfıra bölmə xətası:**\
   `divideByZero()` funksiyasında sıfıra bölmə cəhdi olur, bu isə `panic` vəziyyətinə səbəb olur. Lakin, `recover()` bu `panic`i ələ keçirir və proqramın dayanmadan davam etməsinə imkan yaradır.
3. **Nəticə:**\
   Proqram bu vəziyyəti tutaraq "`Panic yakalandı: runtime error: integer divide by zero`" kimi bir mesaj göstərir və proqram sonrakı kodu icra etməyə davam edir.

Bu yanaşma proqramların gözlənilməz xətaları idarə etməsinə və stabil qalmasına kömək edir.


# Defer

`defer`, Go dilindəki xüsusi bir açar sözdür və müəyyən bir funksiyanın sonunda icra ediləcək funksiyaları və ya ifadələri təyin etmək üçün istifadə olunur. Defer ifadələri, funksiyanın sonunda nə olursa olsun, yəni funksiyanın hər hansı bir səbəbdən sona çatması halında belə icra ediləcəkdir.

Defer ifadələrini istifadə edərək, bir funksiyanın sonunda açılmış faylları, bağlanmamış verilənlər bazası bağlantılarını, şəbəkə bağlantılarını və s. tez və təhlükəsiz bir şəkildə bağlamaq mümkündür.

Nümunə olaraq, bir fayl açılır və `defer` ilə funksiyanın sonunda fayl bağlanılır:

```golang
package main

import (
	"fmt"
	"os"
)

func main() {
	
	f, err := os.Open("example.txt")
	if err != nil {
		fmt.Println(err)
		return
	}
	
	defer f.Close()

	// faylı oxuma
	b := make([]byte, 1024)
	n, err := f.Read(b)
	if err != nil {
		fmt.Println(err)
		return
	}
	
	fmt.Println(string(b[:n]))
}
```

Bu nümunədə, `os.Open` funksiyası istifadə edilərək "example.txt" faylı açılır. `defer` ifadəsi istifadə edilərək fayl bağlama əməliyyatı funksiyanın sonunda icra ediləcək şəkildə planlaşdırılır. Daha sonra, `Read` funksiyası istifadə edilərək faylın məzmunu oxunur və ekrana çap edilir.

Burada diqqət edilməli mühüm bir məqam, `defer` ifadəsinin ən sona yazılmamasıdır. `defer` ifadəsi, bağlanacaq olan faylı açan ifadə ilə eyni blokda olmalıdır. Əks halda, `defer` ifadəsi funksiyanın sonuna qədər gözləməyə davam edəcəkdir.


# String Functions

Golang, string məlumatları üzərində əməliyyatlar aparmaq üçün bir çox hazır funksiyalar təqdim edir. Bu funksiyalar, string məlumatlarını parçalamaq, birləşdirmək, müqayisə etmək, axtarmaq, dəyişdirmək və daha bir çox əməliyyat aparmaq üçün istifadə edilə bilər.

Bəzi çox istifadə olunan string funksiyaları bunlardır:

* `len(s string) int`: Verilmiş string'in uzunluğunu qaytarır.
* `s[i] byte`: Verilmiş string'dəki göstərilən indeks nömrəsindəki simvolu qaytarır.
* `s + t`: İki string'i birləşdirir.
* `strings.Split(s, sep string) []string`: Verilmiş string'i, göstərilən ayırıcı simvollara görə parçalayır və bir array olaraq return edir.
* `strings.Join(a []string, sep string) string`: Verilmiş string arrayini, göstərilən ayırıcı simvol ilə birləşdirir və tək bir string olaraq return edir.
* `strings.Contains(s, substr string) bool`: Verilmiş string daxilində, göstərilən alt string'in olub olmadığını yoxlayır.
* `strings.HasPrefix(s, prefix string) bool`: Verilmiş string'in, göstərilən prefix ilə başlayıb başlamadığını yoxlayır.
* `strings.HasSuffix(s, suffix string) bool`: Verilmiş string'in, göstərilən suffix ilə bitib-bitmədiyini yoxlayır.
* `strings.Replace(s, old, new string, n int) string`: Verilmiş string'də, göstərilən köhnə string'i, göstərilən yeni string ilə dəyişdirir. İstəyə bağlı olaraq, göstərilən sayda dəyişdirmə edir.

Nümunə olaraq, bir string daxilində müəyyən bir xarakter dizisinin neçə dəfə keçdiyini tapmaq üçün `strings.Count` funksiyası istifadə edilə bilər:

```golang
package main

import (
    "fmt"
    s "strings"
)

var p = fmt.Println

func main() {

    p("Contains:  ", s.Contains("test", "es"))
    p("Count:     ", s.Count("test", "t"))
    p("HasPrefix: ", s.HasPrefix("test", "te"))
    p("HasSuffix: ", s.HasSuffix("test", "st"))
    p("Index:     ", s.Index("test", "e"))
    p("Join:      ", s.Join([]string{"a", "b"}, "-"))
    p("Repeat:    ", s.Repeat("a", 5))
    p("Replace:   ", s.Replace("foo", "o", "0", -1))
    p("Replace:   ", s.Replace("foo", "o", "0", 1))
    p("Split:     ", s.Split("a-b-c-d-e", "-"))
    p("ToLower:   ", s.ToLower("TEST"))
    p("ToUpper:   ", s.ToUpper("test"))
}
```

Output:

```go
Contains:   true
Count:      2
HasPrefix:  true
HasSuffix:  true
Index:      1
Join:       a-b
Repeat:     aaaaa
Replace:    f00
Replace:    f0o
Split:      [a b c d e]
ToLower:    test
ToUpper:    TEST
```


# Recover

`recover` funksiyası, bir Go proqramının işləmə müddətində meydana gələn panic-ləri idarə etmək üçün istifadə olunur. Əgər bir panic baş verərsə, `recover` funksiyası panic-in baş verdiyi funksiyanın içində çağırılarsa, panic səbəbindən dayandırılmış əməliyyatın idarəsini geri alır və proqramın normal şəkildə davam etməsini təmin edir.

`recover` funksiyası adətən `defer` ifadələri ilə birlikdə istifadə olunur. Bu sayədə, mümkün bir panic vəziyyətində proqramın idarəsi `defer` ifadələrindəki əməliyyatlardan ən sonuncusu olan `recover` funksiyasına ötürülür və beləliklə proqramın davam etməsi təmin edilir.

Nümunə olaraq, bir panic baş verdikdə proqramın işləməsini dayandırmaq əvəzinə, `recover` funksiyası vasitəsilə proqramın normal şəkildə davam etməsi təmin edilə bilər:

```golang
package main

import "fmt"

func main() {
    fmt.Println("Program starting...")

    // Defer ifadəsi istifadə edərək panic vəziyyətlərində işlədiləcək funksiyanı müəyyən edirik.
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Panic occurred: This is a panic!:", r)
        }
    }()

    fmt.Println("Program continues...")

    // Burada bilərəkdən panic yaradırıq.
    panic("This is a panic")
}
```

Bu nümunədə, `panic` ifadəsi istifadə edilərək bilərəkdən bir panic yaradılır. `defer` ifadəsi istifadə edərək panic vəziyyətlərində işlədiləcək bir funksiya müəyyən edilir. `recover` funksiyası, funksiyanın içində çağırıldığı zaman, panic vəziyyətindən geri dönən dəyəri alır və `if` strukturu istifadə edilərək bu dəyər ilə işləyir.

```
Program starting...
Program continues...
Panic occurred: This is a panic!
This is a panic
```

**String Functions**

Golang, string məlumatları üzərində əməliyyatlar aparmaq üçün bir çox hazır funksiyalar təqdim edir. Bu funksiyalar, string məlumatlarını parçalamaq, birləşdirmək, müqayisə etmək, axtarmaq, dəyişdirmək və daha bir çox əməliyyat aparmaq üçün istifadə edilə bilər.

Bəzi çox istifadə olunan string funksiyaları bunlardır:

* `len(s string) int`: Verilmiş string'in uzunluğunu qaytarır.
* `s[i] byte`: Verilmiş string'dəki göstərilən indeks nömrəsindəki simvolu qaytarır.
* `s + t`: İki string'i birləşdirir.
* `strings.Split(s, sep string) []string`: Verilmiş string'i, göstərilən ayırıcı simvollara görə parçalayır və bir array olaraq return edir.
* `strings.Join(a []string, sep string) string`: Verilmiş string arrayini, göstərilən ayırıcı simvol ilə birləşdirir və tək bir string olaraq return edir.
* `strings.Contains(s, substr string) bool`: Verilmiş string daxilində, göstərilən alt string'in olub olmadığını yoxlayır.
* `strings.HasPrefix(s, prefix string) bool`: Verilmiş string'in, göstərilən prefix ilə başlayıb başlamadığını yoxlayır.
* `strings.HasSuffix(s, suffix string) bool`: Verilmiş string'in, göstərilən suffix ilə bitib-bitmədiyini yoxlayır.
* `strings.Replace(s, old, new string, n int) string`: Verilmiş string'də, göstərilən köhnə string'i, göstərilən yeni string ilə dəyişdirir. İstəyə bağlı olaraq, göstərilən sayda dəyişdirmə edir.

Nümunə olaraq, bir string daxilində müəyyən bir xarakter dizisinin neçə dəfə keçdiyini tapmaq üçün `strings.Count` funksiyası istifadə edilə bilər:

```golang
package main

import (
    "fmt"
    s "strings"
)

var p = fmt.Println

func main() {

    p("Contains:  ", s.Contains("test", "es"))
    p("Count:     ", s.Count("test", "t"))
    p("HasPrefix: ", s.HasPrefix("test", "te"))
    p("HasSuffix: ", s.HasSuffix("test", "st"))
    p("Index:     ", s.Index("test", "e"))
    p("Join:      ", s.Join([]string{"a", "b"}, "-"))
    p("Repeat:    ", s.Repeat("a", 5))
    p("Replace:   ", s.Replace("foo", "o", "0", -1))
    p("Replace:   ", s.Replace("foo", "o", "0", 1))
    p("Split:     ", s.Split("a-b-c-d-e", "-"))
    p("ToLower:   ", s.ToLower("TEST"))
    p("ToUpper:   ", s.ToUpper("test"))
}
```

Output:

```go
Contains:   true
Count:      2
HasPrefix:  true
HasSuffix:  true
Index:      1
Join:       a-b
Repeat:     aaaaa
Replace:    f00
Replace:    f0o
Split:      [a b c d e]
ToLower:    test
ToUpper:    TEST
```


# Text Templates

Template (şablon) faylları, Go dilində mətn əsaslı məlumatları formatlamaq üçün istifadə olunur. Bir şablon faylı, bir mətn sənədindəki məlumatların bir qismini yer tutucular və ya ifadələrlə dəyişdirərək yaradılır. Bu əməliyyat, bir şablon faylını bir məlumat strukturu ilə birləşdirərək həyata keçirilir.

Məsələn, bir e-poçt göndərimi zamanı, bir şablon faylı istifadə edərək mesajın məzmununu müəyyən bir formatda yaratmaq olar. Şablon faylında, dəyişənlər və funksiyalar istifadə edərək yer tutucular və ifadələr təyin edilə bilər. Daha sonra, bu dəyişənlər və funksiyalar real məlumat strukturu ilə birləşdirilərək nəticədə tamamlanmış mətn sənədi yaradılır.

Go dilində, şablon faylları `text/template` və `html/template` paketləri ilə yaradılır. Bu paketlər, şablon fayllarının yaradılmasını və işlənməsini təmin edən bir çox funksiya təqdim edir.

```golang
package main

import (
    "fmt"
    "os"
    "text/template"
)

type Person struct {
    Name    string
    Age     int
    Country string
}

func main() {
    person := Person{
        Name:    "John",
        Age:     30,
        Country: "USA",
    }

    tpl := "My name is {{.Name}} and I am {{.Age}} years old. I live in {{.Country}}.\n"
    t := template.Must(template.New("person").Parse(tpl))

    err := t.Execute(os.Stdout, person)
    if err != nil {
        fmt.Println("Error executing template:", err)
    }
}
```

Bu nümunədə, `Person` adlı bir məlumat strukturu təyin edilmişdir. Sonra, `tpl` dəyişənində, şablonun bir mətn parçası mövcuddur. Şablon içərisində istifadə olunacaq dəyişənlər `{{.}}` işarələri arasına yazılır. Bu dəyişənlərə məlumat strukturundakı sahələr təyin ediləcəkdir.

Daha sonra, `template.Must()` funksiyası istifadə edilərək yeni bir `Template` obyekti yaradılır və şablon mətnini `Parse()` funksiyası ilə bu obyektə təyin edilir. Sonra, `Execute()` funksiyası istifadə edilərək, `Template` obyekti, məlumat strukturu və `os.Stdout` (standart çıxış) printeri istifadə edilərək işlənir və nəticə ekrana çıxarılır.

Output:

```go
My name is John and I am 30 years old. I live in USA.
```


# Json

JSON (JavaScript Object Notation), yüngül bir veri mübadiləsi formatıdır və insanlar tərəfindən oxunması və yazılması asandır. Go dilində, `encoding/json` paketi JSON məlumatları ilə işləmək üçün istifadə edilə bilər.

JSON məlumatları, açar/dəyər cütləri və ya siyahılardan ibarət ola bilər. Açarlar bir string, dəyərlər isə JSON məlumatı ola bilər. JSON məlumatları `{}` (qıvrım mötərizələr) ilə göstərilən obyektlər şəklində də ola bilər.

Misal JSON məlumatı belə görünə bilər:

```json
{
    "name": "John Doe",
    "age": 30,
    "city": "New York"
}
```

Bu məlumat, `name`, `age` və `city` adlı üç açar saxlayır. `name` açarı bir string dəyəri, `age` açarı bir sayısal dəyər və `city` açarı da bir string dəyəridir.

Go dilində, JSON məlumatları üçün `struct` quruluşu istifadə edilə bilər. `struct` içərisindəki sahələr JSON məlumatındakı açarlarla uyğunlaşdırıla bilər. Daha sonra, `encoding/json` paketində olan `Marshal()` və `Unmarshal()` funksiyaları istifadə edilərək, Go obyekti və JSON məlumatı arasında dönüşüm edilə bilər.

Misal üçün, bir `Person` struct'ını tanıdaq və bu struct-ı JSON məlumatı ilə uyğunlaşdıraq:

```golang
type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    City string `json:"city"`
}
```

Burada, `Person` struct-ının sahələri JSON məlumatındakı açarlarla uyğunlaşdırılmışdır. `Name` sahəsi üçün `name`, `Age` sahəsi üçün `age`, `City` sahəsi üçün isə `city` açarları istifadə olunmuşdur.

Bu struct-ı JSON məlumatına çevirmək üçün, `json.Marshal()` funksiyası istifadə edilə bilər:

```go
person := Person{Name: "John Doe", Age: 30, City: "New York"}
jsonData, err := json.Marshal(person)
if err != nil {
    fmt.Println("Error encoding JSON:", err)
}
fmt.Println(string(jsonData)) // {"name":"John Doe","age":30,"city":"New York"}
```

Burada, `Person` struct-ı bir `person` dəyişəni olaraq təyin edilir və `json.Marshal()` funksiyası istifadə edilərək `person` obyekti JSON məlumatına çevrilir. Əldə edilən JSON məlumatı `string()` funksiyası istifadə edilərək string tipinə çevrilir və ekrana çap olunur.

Başqa bir misal isə, JSON məlumatlarını Go məlumat strukturlarına çevirmək və əksinə etmək üçün `json.Marshal()` və `json.Unmarshal()` funksiyalarını istifadə etməkdir. Məsələn:

```golang
package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    p1 := Person{"Alice", 30}
    jsonStr, err := json.Marshal(p1)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(jsonStr))

  
    var p2 Person
    jsonStr = []byte(`{"Name":"Bob","Age":40}`)
    err = json.Unmarshal(jsonStr, &p2)
    if err != nil {
        panic(err)
    }
    fmt.Println(p2)
}
```

Bu nümunədə, `Person` adlı bir struktur təyin edilir və `json.Marshal()` və `json.Unmarshal()` funksiyaları istifadə edilərək bu struktur JSON məlumatı olaraq kodlanır və təhlil edilir.

### **Output**

```go
{"Name":"Alice","Age":30}
{Bob 40}
```


# Mysql

Golang ilə MySQL verilənlər bazasında necə işləmək barədə təcrübəmi paylaşacağam. Aşağıdakı addımlarla həm MySQL serverinə necə qoşulacağınızı, həm də CRUD (yaratma, oxuma, yeniləmə, silmə) əməliyyatlarını, həmçinin hazırlanan ifadələr və transaction idarəetməsini nümunələrlə izah edəcəyəm.

***

## 1. Başlamazdan Əvvəl: Tələblər və Quraşdırma

### Nə Lazımdır:

* **MySQL Serveri:** İşlək bir MySQL verilənlər bazası (lokal və ya uzaq server).
* **MySQL Go Driver:** [go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) paketini istifadə edəcəyik.

### Quraşdırma:

Terminalınızda aşağıdakı əmri işə salın:

```bash
go get -u github.com/go-sql-driver/mysql
```

Bu driver Golang-ın `database/sql` paketinin MySQL ilə işləməsini təmin edir.

***

## 2. Verilənlər Bazası ilə Əlaqə

İlk addımımız MySQL serverinə qoşulmaqdır. Bunun üçün DSN (Data Source Name) adlanan məlumatı müəyyən etmək lazımdır. DSN-də istifadəçi adı, parol, serverin ünvanı, port və verilənlər bazasının adı yer alır.

### Məsələn:

```go
package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/go-sql-driver/mysql" // MySQL driverini əlavə edirik
)

func main() {
	// DSN: "user:pass@tcp(127.0.0.1:3306)/database_name"
	dsn := "istifadeci:password@tcp(127.0.0.1:3306)/database_name"
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		log.Fatal("Bağlantı xətası:", err)
	}
	defer db.Close()

	// Əlaqəni sınayırıq
	if err := db.Ping(); err != nil {
		log.Fatal("Verilənlər bazasına qoşularkən problem:", err)
	}

	fmt.Println("MySQL verilənlər bazasına uğurla qoşuldunuz!")
}
```

> **Qeyd:** `sql.Open` funksiya ilə əlaqə yaradılır, amma əlaqənin həqiqətən işlədiyini `db.Ping()` ilə yoxlamaq vacibdir.

***

## 3. CRUD Əməliyyatları

### a) Məlumat Əlavə Etmək (Create)

Yeni məlumat əlavə etmək üçün `INSERT` sorğusundan istifadə edirik.

```go
// Məlumat əlavə etmək nümunəsi
query := "INSERT INTO users (name, email) VALUES (?, ?)"
result, err := db.Exec(query, "Khanbala", "khanbala@example.com")
if err != nil {
	log.Fatal("Məlumat əlavə edilərkən xəta baş verdi:", err)
}

// Əlavə olunan son ID-ni əldə edirik
lastInsertID, err := result.LastInsertId()
if err != nil {
	log.Fatal("Son əlavə olunan ID əldə edilərkən xəta:", err)
}
fmt.Println("Əlavə olunan məlumatınß ID-si:", lastInsertID)
```

### b) Məlumat Oxumaq (Read)

Mövcud məlumatları oxumaq üçün `SELECT` sorğusunu istifadə edirik.

```go
// Məlumat oxumaq nümunəsi
rows, err := db.Query("SELECT id, name, email FROM users")
if err != nil {
	log.Fatal("Sorğu zamanı xəta baş verdi:", err)
}
defer rows.Close()

for rows.Next() {
	var id int
	var name, email string
	if err := rows.Scan(&id, &name, &email); err != nil {
		log.Fatal("Məlumat oxunarkən xəta:", err)
	}
	fmt.Printf("ID: %d, Name: %s, Email: %s\n", id, name, email)
}

// Satırların oxunması zamanı yaranan xətaları yoxlayırıq
if err := rows.Err(); err != nil {
	log.Fatal("Oxuma zamanı xəta baş verdi:", err)
}
```

### c) Məlumat Yeniləmək (Update)

Mövcud məlumat üzərində dəyişiklik etmək üçün `UPDATE` sorğusundan istifadə edirik.

```go
// Məlumat yeniləmə nümunəsi
updateQuery := "UPDATE users SET email = ? WHERE id = ?"
res, err := db.Exec(updateQuery, "yeni_email@example.com", 1)
if err != nil {
	log.Fatal("Yeniləmə zamanı xəta baş verdi:", err)
}

// Neçə sətirin yeniləndiyini öyrənirik
affectedRows, err := res.RowsAffected()
if err != nil {
	log.Fatal("Əlavə edilən sətir sayını əldə edərkən xəta:", err)
}
fmt.Printf("Yenilənən sətir sayı: %d\n", affectedRows)
```

### d) Məlumat Silmək (Delete)

Artıq lazım olmayan məlumatı silmək üçün `DELETE` sorğusu istifadə olunur.

```go
// Məlumat silmə nümunəsi
deleteQuery := "DELETE FROM users WHERE id = ?"
res, err = db.Exec(deleteQuery, 1)
if err != nil {
	log.Fatal("Silmə əməliyyatı zamanı xəta baş verdi:", err)
}

deletedRows, err := res.RowsAffected()
if err != nil {
	log.Fatal("Silinən sətir sayını əldə edərkən xəta:", err)
}
fmt.Printf("Silinən sətir sayı: %d\n", deletedRows)
```

***

## 4. Hazırlanan İfadələr (Prepared Statements)

Təkrarlanan sorğular üçün hazırlanan ifadələr performansı artırır və SQL enjeksiyasının qarşısını alır. Gəlin, bir neçə məlumatı hazırlanan ifadə ilə necə əlavə edəcəyimizə baxaq.

```go
// Hazırlanan ifadə ilə məlumat əlavə etmək
stmt, err := db.Prepare("INSERT INTO users (name, email) VALUES (?, ?)")
if err != nil {
	log.Fatal("İfadə hazırlanarkən xəta baş verdi:", err)
}
defer stmt.Close()

// Birdən çox istifadəçi əlavə etmək nümunəsi
users := []struct {
	name  string
	email string
}{
	{"Khanbala", "khanbala@example.com"},
}

for _, user := range users {
	res, err := stmt.Exec(user.name, user.email)
	if err != nil {
		log.Println("Məlumat əlavə edilərkən xəta:", err)
		continue
	}
	id, _ := res.LastInsertId()
	fmt.Printf("Əlavə olunan istifadəçi ID-si: %d\n", id)
}
```

***

## 5. Transaction (Əməliyyat) İdarəetməsi

Transaction istifadə edərək, birdən çox əməliyyatın ya hamısının uğurla, ya da heç birinin tətbiq olunmasını təmin edirik. Bu, məlumatın tutarlılığını qorumağa kömək edir.

```go
// Transaction başlatma
tx, err := db.Begin()
if err != nil {
	log.Fatal("Transaction başladılarkən xəta:", err)
}

// İlk əməliyyat: məlumat əlavə etmək
_, err = tx.Exec("INSERT INTO users (name, email) VALUES (?, ?)", "Mushfig", "mushfig@example.com")
if err != nil {
	tx.Rollback() // Xəta olduqda bütün əməliyyatları geri alırıq
	log.Fatal("Transaction zamanı xəta (INSERT):", err)
}

// İkinci əməliyyat: məlumat yeniləmək
_, err = tx.Exec("UPDATE users SET email = ? WHERE name = ?", "mushfig_yeni@example.com", "Mushfig")
if err != nil {
	tx.Rollback()
	log.Fatal("Transaction zamanı xəta (UPDATE):", err)
}

// Transaction-u təsdiqləyirik
if err := tx.Commit(); err != nil {
	log.Fatal("Transaction commit edilərkən xəta:", err)
}

fmt.Println("Transaction uğurla tamamlandı!")
```

***

## 6. Xəta İdarəetməsi və Ən Yaxşı Təcrübələr

* **Xəta Yoxlanışı:** Hər əməliyyatdan sonra `err` yoxlayaraq proqramınızın möhkəm işləməsini təmin edin.
* **Bağlantı İdarəetməsi:** İşiniz bitdikdən sonra `defer db.Close()` istifadə edərək açıq əlaqələrin düzgün bağlanmasını unutmayın.
* **Hazırlanan İfadələr:** Təkrarlanan sorğularda hazırlanan ifadələrdən istifadə edərək həm təhlükəsizliyi, həm də performansı artırın.
* **Transaction İstifadəsi:** Əməliyyatların bir-birinə bağlı olduğu hallarda transaction istifadə edin ki, hər hansı bir problem yarandıqda bütün əməliyyatlar geri alınsın.


# Postgres

Aşağıda Golang ilə PostgreSQL verilənlər bazasında necə işləmək barədə təcrübəmi paylaşacağam. Addım-addım izah edəcəyəm ki, PostgreSQL serverinə necə qoşulursunuz, CRUD (yaratma, oxuma, yeniləmə, silmə) əməliyyatlarını necə yerinə yetirirsiniz, həmçinin hazırlanan ifadələr və transaction idarəetməsini necə tətbiq edirsiniz.

***

### 1. Başlamazdan Əvvəl: Tələblər və Quraşdırma

#### Nə Lazımdır:

* **PostgreSQL Serveri:** İşlək bir PostgreSQL verilənlər bazası (lokal və ya uzaq server).
* **PostgreSQL Go Driver:** [lib/pq](https://github.com/lib/pq) paketini istifadə edəcəyik.

#### Quraşdırma:

Terminalınızda aşağıdakı əmri işə salın:

```bash
go get -u github.com/lib/pq
```

Bu driver Golang-ın `database/sql` paketinin PostgreSQL ilə işləməsini təmin edir.

***

### 2. Verilənlər Bazası ilə Əlaqə

İlk addım PostgreSQL serverinə qoşulmaqdır. Bunun üçün DSN (Data Source Name) formatında məlumatları təyin etmək lazımdır. DSN-də istifadəçi adı, parol, serverin ünvanı, port və verilənlər bazasının adı yer alır.

#### Məsələn:

```go
package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/lib/pq" // PostgreSQL driverini əlavə edirik
)

func main() {
	// DSN formatı: "postgres://user_name:password@localhost:5432/database_name?sslmode=disable"
	dsn := "postgres://user_name:password@localhost:5432/database_name?sslmode=disable"
	db, err := sql.Open("postgres", dsn)
	if err != nil {
		log.Fatal("Bağlantı xətası:", err)
	}
	defer db.Close()

	// Əlaqəni sınayırıq
	if err := db.Ping(); err != nil {
		log.Fatal("Verilənlər bazasına qoşularkən problem:", err)
	}

	fmt.Println("PostgreSQL verilənlər bazasına uğurla qoşuldunuz!")
}
```

> **Qeyd:** `sql.Open` funksiyası əlaqəni dərhal yoxlamır, ona görə də `db.Ping()` istifadə edərək əlaqənin aktiv olduğunu təsdiq etmək vacibdir.

***

### 3. CRUD Əməliyyatları

#### a) Məlumat Əlavə Etmək (Create)

Yeni məlumat əlavə etmək üçün `INSERT` sorğusundan istifadə edirik.

```go
// Məlumat əlavə etmək nümunəsi
query := "INSERT INTO users (name, email) VALUES ($1, $2)"
result, err := db.Exec(query, "Khanbala", "khanbala@example.com")
if err != nil {
	log.Fatal("Məlumat əlavə edilərkən xəta baş verdi:", err)
}

// Əlavə olunan son ID-ni əldə edirik (PostgreSQL-də qaytarılması üçün RETURNING klauzulası lazımdır)
fmt.Println("Məlumat əlavə olundu!")
```

> **Qeyd:** PostgreSQL-də son əlavə olunan ID-ni əldə etmək üçün sorğunu `RETURNING id` klauzulası ilə yazmaq tövsiyə olunur. Məsələn:
>
> ```go
> query := "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id"
> var lastInsertID int
> err = db.QueryRow(query, "Khanbala", "khanbala@example.com").Scan(&lastInsertID)
> if err != nil {
>     log.Fatal("ID əldə edilərkən xəta:", err)
> }
> fmt.Println("Əlavə olunan məlumatın ID-si:", lastInsertID)
> ```

#### b) Məlumat Oxumaq (Read)

Mövcud məlumatları oxumaq üçün `SELECT` sorğusunu istifadə edirik.

```go
// Məlumat oxumaq nümunəsi
rows, err := db.Query("SELECT id, name, email FROM users")
if err != nil {
	log.Fatal("Sorğu zamanı xəta baş verdi:", err)
}
defer rows.Close()

for rows.Next() {
	var id int
	var name, email string
	if err := rows.Scan(&id, &name, &email); err != nil {
		log.Fatal("Məlumat oxunarkən xəta:", err)
	}
	fmt.Printf("ID: %d, Name: %s, Email: %s\n", id, name, email)
}

// Satırların oxunması zamanı yaranan xətaları yoxlayırıq
if err := rows.Err(); err != nil {
	log.Fatal("Oxuma zamanı xəta baş verdi:", err)
}
```

#### c) Məlumat Yeniləmək (Update)

Mövcud məlumat üzərində dəyişiklik etmək üçün `UPDATE` sorğusundan istifadə edirik.

```go
// Məlumat yeniləmə nümunəsi
updateQuery := "UPDATE users SET email = $1 WHERE id = $2"
res, err := db.Exec(updateQuery, "yeni_email@example.com", 1)
if err != nil {
	log.Fatal("Yeniləmə zamanı xəta baş verdi:", err)
}

// Neçə sətirin yeniləndiyini öyrənirik
affectedRows, err := res.RowsAffected()
if err != nil {
	log.Fatal("Əlavə edilən əldə sayını əldə edərkən xəta:", err)
}
fmt.Printf("Yenilənən sətir sayı: %d\n", affectedRows)
```

#### d) Məlumat Silmək (Delete)

Artıq lazım olmayan məlumatı silmək üçün `DELETE` sorğusunu istifadə edirik.

```go
// Məlumat silmə nümunəsi
deleteQuery := "DELETE FROM users WHERE id = $1"
res, err = db.Exec(deleteQuery, 1)
if err != nil {
	log.Fatal("Silmə əməliyyatı zamanı xəta baş verdi:", err)
}

deletedRows, err := res.RowsAffected()
if err != nil {
	log.Fatal("Silinən sətir sayını əldə edərkən xəta:", err)
}
fmt.Printf("Silinən satır sayı: %d\n", deletedRows)
```

***

### 4. Hazırlanan İfadələr (Prepared Statements)

Təkrarlanan sorğular üçün hazırlanan ifadələr performansı artırır və SQL enjeksiyasının qarşısını alır. Gəlin, bir neçə məlumatı hazırlanan ifadə ilə necə əlavə edəcəyimizə baxaq.

```go
// Hazırlanan ifadə ilə məlumat əlavə etmək
stmt, err := db.Prepare("INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id")
if err != nil {
	log.Fatal("İfadə hazırlanarkən xəta baş verdi:", err)
}
defer stmt.Close()

// Birdən çox istifadəçi əlavə etmək nümunəsi
users := []struct {
	name  string
	email string
}{
	{"Khanbala", "khanbala@example.com"},
}

for _, user := range users {
	var id int
	err := stmt.QueryRow(user.name, user.email).Scan(&id)
	if err != nil {
		log.Println("Məlumat əlavə edilərkən xəta:", err)
		continue
	}
	fmt.Printf("Əlavə olunan istifadəçi ID-si: %d\n", id)
}
```

***

### 5. Transaction (Əməliyyat) İdarəetməsi

Transaction istifadə edərək, birdən çox əməliyyatın ya hamısının uğurla, ya da heç birinin tətbiq olunmasını təmin edirik. Bu, məlumatın tutarlılığını qorumağa kömək edir.

```go
// Transaction başlatma
tx, err := db.Begin()
if err != nil {
	log.Fatal("Transaction başladılarkən xəta:", err)
}

// İlk əməliyyat: məlumat əlavə etmək
insertQuery := "INSERT INTO users (name, email) VALUES ($1, $2)"
_, err = tx.Exec(insertQuery, "Mushfig", "mushfig@example.com")
if err != nil {
	tx.Rollback() // Xəta olduqda bütün əməliyyatları geri alırıq
	log.Fatal("Transaction zamanı xəta (INSERT):", err)
}
ß
// İkinci əməliyyat: məlumat yeniləmək
updateQuery := "UPDATE users SET email = $1 WHERE name = $2"
_, err = tx.Exec(updateQuery, "mushfig_yeni@example.com", "Mushfig")
if err != nil {
	tx.Rollback()
	log.Fatal("Transaction zamanı xəta (UPDATE):", err)
}

// Transaction-u təsdiqləyirik
if err := tx.Commit(); err != nil {
	log.Fatal("Transaction commit edilərkən xəta:", err)
}

fmt.Println("Transaction uğurla tamamlandı!")
```

***

### 6. Xəta İdarəetməsi və Ən Yaxşı Təcrübələr

* **Xəta Yoxlanışı:** Hər əməliyyatdan sonra `err` yoxlayaraq proqramınızın sabit işləməsini təmin edin.
* **Bağlantı İdarəetməsi:** İşiniz bitdikdən sonra `defer db.Close()` istifadə edərək açıq əlaqələrin düzgün bağlanmasını unutmayın.
* **Hazırlanan İfadələr:** Təkrarlanan sorğularda hazırlanan ifadələrdən istifadə edərək həm təhlükəsizliyi, həm də performansı artırın.
* **Transaction İstifadəsi:** Əməliyyatların bir-birinə bağlı olduğu hallarda transaction istifadə edin ki, hər hansı bir problem yarandıqda bütün əməliyyatlar geri alınsın.


# Mssql

Aşağıda Golang ilə MSSQL (Microsoft SQL Server) verilənlər bazasında necə işləmək barədə təcrübəmi paylaşacağam. Addım-addım izah edəcəyəm ki, MSSQL serverinə necə qoşulursunuz, CRUD (yaratma, oxuma, yeniləmə, silmə) əməliyyatlarını necə yerinə yetirirsiniz, həmçinin hazırlanan ifadələr və transaction idarəetməsini necə tətbiq edirsiniz.

***

### 1. Başlamazdan Əvvəl: Tələblər və Quraşdırma

#### Nə Lazımdır:

* **MSSQL Serveri:** İşlək bir Microsoft SQL Server (lokal və ya uzaq server).
* **MSSQL Go Driver:** [denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) paketini istifadə edəcəyik.

#### Quraşdırma:

Terminalınızda aşağıdakı əmri işə salın:

```bash
go get -u github.com/denisenkom/go-mssqldb
```

Bu driver, Golang-ın `database/sql` paketinin MSSQL ilə işləməsini təmin edir.

***

### 2. Verilənlər Bazası ilə Əlaqə

İlk addım MSSQL serverinə qoşulmaqdır. Bunun üçün DSN (Data Source Name) formatında məlumatları təyin etmək lazımdır. DSN-də istifadəçi adı, parol, serverin ünvanı, port və verilənlər bazasının adı yer alır.

#### Məsələn:

```go
package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/denisenkom/go-mssqldb" // MSSQL driverini əlavə edirik
)

func main() {
	// DSN formatı: "sqlserver://user_name:password@localhost:1433?database=database_name"
	dsn := "sqlserver://user_name:password@localhost:1433?database=database_name"
	db, err := sql.Open("sqlserver", dsn)
	if err != nil {
		log.Fatal("Bağlantı xətası:", err)
	}
	defer db.Close()

	// Əlaqəni sınayırıq
	if err := db.Ping(); err != nil {
		log.Fatal("Verilənlər bazasına qoşularkən problem:", err)
	}

	fmt.Println("MSSQL verilənlər bazasına uğurla qoşuldunuz!")
}
```

> **Qeyd:** `sql.Open` funksiyası əlaqəni dərhal yoxlamır, ona görə də `db.Ping()` istifadə edərək əlaqənin aktiv olduğunu təsdiq etmək vacibdir.

***

### 3. CRUD Əməliyyatları

#### a) Məlumat Əlavə Etmək (Create)

Yeni məlumat əlavə etmək üçün `INSERT` sorğusundan istifadə edirik. MSSQL-də əlavə olunan ID-ni əldə etmək üçün `OUTPUT INSERTED.id` klauzulası istifadə olunur.

```go
// Məlumat əlavə etmək nümunəsi
query := "INSERT INTO users (name, email) OUTPUT INSERTED.id VALUES (?, ?)"
var lastInsertID int
err := db.QueryRow(query, "khanbala", "khanbala@example.com").Scan(&lastInsertID)
if err != nil {
	log.Fatal("Məlumat əlavə edilərkən xəta baş verdi:", err)
}
fmt.Printf("Əlavə olunan məlumatın ID-si: %d\n", lastInsertID)
```

#### b) Məlumat Oxumaq (Read)

Mövcud məlumatları oxumaq üçün `SELECT` sorğusundan istifadə edirik.

```go
// Məlumat oxumaq nümunəsi
rows, err := db.Query("SELECT id, name, email FROM users")
if err != nil {
	log.Fatal("Sorğu zamanı xəta baş verdi:", err)
}
defer rows.Close()

for rows.Next() {
	var id int
	var name, email string
	if err := rows.Scan(&id, &name, &email); err != nil {
		log.Fatal("Məlumat oxunarkən xəta:", err)
	}
	fmt.Printf("ID: %d, Name: %s, Email: %s\n", id, name, email)
}

if err := rows.Err(); err != nil {
	log.Fatal("Oxuma zamanı xəta baş verdi:", err)
}
```

#### c) Məlumat Yeniləmək (Update)

Mövcud məlumat üzərində dəyişiklik etmək üçün `UPDATE` sorğusundan istifadə edirik.

```go
// Məlumat yeniləmə nümunəsi
updateQuery := "UPDATE users SET email = ? WHERE id = ?"
res, err := db.Exec(updateQuery, "yeni_email@example.com", 1)
if err != nil {
	log.Fatal("Yeniləmə zamanı xəta baş verdi:", err)
}

affectedRows, err := res.RowsAffected()
if err != nil {
	log.Fatal("Yenilənən satır sayını əldə edərkən xəta:", err)
}
fmt.Printf("Yenilənən sətir sayı: %d\n", affectedRows)
```

#### d) Məlumat Silmək (Delete)

Artıq lazım olmayan məlumatı silmək üçün `DELETE` sorğusundan istifadə edirik.

```go
// Məlumat silmə nümunəsi
deleteQuery := "DELETE FROM users WHERE id = ?"
res, err = db.Exec(deleteQuery, 1)
if err != nil {
	log.Fatal("Silmə əməliyyatı zamanı xəta baş verdi:", err)
}

deletedRows, err := res.RowsAffected()
if err != nil {
	log.Fatal("Silinən sətir sayını əldə edərkən xəta:", err)
}
fmt.Printf("Silinən sətir sayı: %d\n", deletedRows)
```

***

### 4. Hazırlanan İfadələr (Prepared Statements)

Təkrarlanan sorğular üçün hazırlanan ifadələr performansı artırır və SQL enjeksiyasının qarşısını alır. Gəlin, bir neçə məlumatı hazırlanan ifadə ilə necə əlavə edəcəyimizə baxaq.

```go
// Hazırlanan ifadə ilə məlumat əlavə etmək
stmt, err := db.Prepare("INSERT INTO users (name, email) OUTPUT INSERTED.id VALUES (?, ?)")
if err != nil {
	log.Fatal("İfadə hazırlanarkən xəta baş verdi:", err)
}
defer stmt.Close()

// Birdən çox istifadəçi əlavə etmək nümunəsi
users := []struct {
	name  string
	email string
}{
	{"Khanbala", "khanbala@example.com"},
}

for _, user := range users {
	var id int
	err := stmt.QueryRow(user.name, user.email).Scan(&id)
	if err != nil {
		log.Println("Məlumat əlavə edilərkən xəta:", err)
		continue
	}
	fmt.Printf("Əlavə olunan istifadəçi ID-si: %d\n", id)
}
```

***

### 5. Transaction (Əməliyyat) İdarəetməsi

Transaction istifadə edərək, birdən çox əməliyyatın ya hamısının uğurla, ya da heç birinin tətbiq olunmasını təmin edirik. Bu, məlumatın tutarlılığını qorumağa kömək edir.

```go
// Transaction başlatma
tx, err := db.Begin()
if err != nil {
	log.Fatal("Transaction başladılarkən xəta:", err)
}

// İlk əməliyyat: məlumat əlavə etmək
insertQuery := "INSERT INTO users (name, email) VALUES (?, ?)"
_, err = tx.Exec(insertQuery, "Mushfig", "mushfig@example.com")
if err != nil {
	tx.Rollback() // Xəta olduqda bütün əməliyyatları geri alırıq
	log.Fatal("Transaction zamanı xəta (INSERT):", err)
}

// İkinci əməliyyat: məlumat yeniləmək
updateQuery := "UPDATE users SET email = ? WHERE name = ?"
_, err = tx.Exec(updateQuery, "mushfig_yeni@example.com", "Mushfig")
if err != nil {
	tx.Rollback()
	log.Fatal("Transaction zamanı xəta (UPDATE):", err)
}

// Transaction-u təsdiqləyirik
if err := tx.Commit(); err != nil {
	log.Fatal("Transaction commit edilərkən xəta:", err)
}

fmt.Println("Transaction uğurla tamamlandı!")
```

***

### 6. Xəta İdarəetməsi və Ən Yaxşı Təcrübələr

* **Xəta Yoxlanışı:** Hər əməliyyatdan sonra `err` yoxlayaraq proqramınızın sabit işləməsini təmin edin.
* **Bağlantı İdarəetməsi:** İşiniz bitdikdən sonra `defer db.Close()` istifadə edərək açıq əlaqələrin düzgün bağlanmasına diqqət yetirin.
* **Hazırlanan İfadələr:** Təkrarlanan sorğularda hazırlanan ifadələrdən istifadə edərək həm təhlükəsizliyi, həm də performansı artırın.
* **Transaction İstifadəsi:** Əməliyyatların bir-birinə bağlı olduğu hallarda transaction istifadə edin ki, hər hansı bir problem yarandıqda bütün əməliyyatlar geri alınsın.


