Підручник Golang: Вивчіть мову програмування Go для початківців

Що таке Go?

Go (також відома як Golang) — мова програмування з відкритим кодом, розроблена Google. Це статично типізована скомпільована мова. Go підтримує паралельне програмування, тобто дозволяє запускати кілька процесів одночасно. Це досягається за допомогою каналів, goroutines тощо. Go Language має збірку сміття, яка сама керує пам’яттю та дозволяє відкладене виконання функцій.

Ми вивчимо всі основи мови Golang у цьому посібнику з вивчення мови Go.

Як завантажити та встановити GO

Крок 1) Перейдіть до https://golang.org/dl/. Завантажте двійковий файл для вашої ОС.

Крок 2) Double клацніть інсталятор і натисніть Запустити.

Крок 3) Натисніть Далі

Крок 4) Виберіть папку встановлення та натисніть «Далі».

Крок 5) Після завершення інсталяції натисніть «Готово».

Крок 6) Після завершення встановлення ви можете перевірити його, відкривши термінал і ввівши

go version

Відобразиться встановлена ​​версія go

Ваша перша програма Go – Go Hello World!

Створіть папку під назвою studyGo. У цьому посібнику з мови Go ми створимо наші програми go в цій папці. Файли Go створюються з розширенням .іди. Ви можете запускати програми Go за допомогою синтаксису

go run <filename>

Створіть файл під назвою first.go, додайте в нього наведений нижче код і збережіть

package main
import ("fmt")

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

Перейдіть до цієї папки у вашому терміналі. Запустіть програму за допомогою команди

іди, біжи першим

Ви можете побачити вихідний друк

Hello World! This is my first Go program

Тепер давайте обговоримо вищезгадану програму.

основний пакет – кожна програма Go Language повинна починатися з назви пакета. Go дозволяє нам використовувати пакунки в інших програмах go і, отже, підтримує повторне використання коду. Виконання програми Go починається з коду всередині пакета під назвою main.

import fmt – імпортує пакет fmt. Цей пакет реалізує функції введення/виведення.

func main() – це функція, з якої починається виконання програми. Головну функцію завжди слід розміщувати в основному пакеті. У main() ви можете написати код усередині { }.

fmt.Println – це друкує текст на екрані за допомогою функції Println fmt.

Примітка: у наведених нижче розділах цього посібника з Go, коли ви згадуєте виконати/запустити код, це означає, що потрібно зберегти код у файлі з розширенням .go і запустити його за допомогою синтаксису

    go run <filename>

Типи даних

Типи (типи даних) представляють тип значення, що зберігається у змінній, тип значення, яке повертає функція, тощо.

У Go Language є три основних типи

Числові типи – Представляти числові значення, які включають цілі числа, значення з плаваючою комою та комплексні значення. Різноманітні числові типи:

int8 – 8-розрядні цілі числа зі знаком.

int16 – 16-розрядні цілі числа зі знаком.

int32 – 32-розрядні цілі числа зі знаком.

int64 – 64-розрядні цілі числа зі знаком.

uint8 – 8-розрядні цілі числа без знаку.

uint16 – 16-розрядні цілі числа без знаку.

uint32 – 32-розрядні цілі числа без знаку.

uint64 – 64-розрядні цілі числа без знаку.

float32 – 32-розрядні числа з плаваючою комою.

float64 – 64-розрядні числа з плаваючою комою.

complex64 – має float32 дійсну та уявну частини.

complex128 – має float32 дійсну та уявну частини.

Типи рядків – Представляє послідовність байтів (символів). Ви можете виконувати різні операції над рядками, як-от конкатенація рядків, вилучення підрядка тощо

Логічні типи – Представляє 2 значення, істинне або хибне.

Інтерфейс Golang

Інтерфейс Golang це набір сигнатур методів, які використовуються типом для реалізації поведінки об’єктів. Основною метою інтерфейсу Golang є надання сигнатур методу з іменами, аргументами та типами повернення. Тип повинен оголосити та реалізувати метод. Інтерфейс у Golang можна оголосити за допомогою ключового слова «інтерфейс».

Змінні

Змінні вказують на область пам'яті, де зберігається певне значення. Параметр типу (у наведеному нижче синтаксисі) представляє тип значення, яке можна зберегти в місці пам’яті.

Змінну можна оголосити за допомогою синтаксису

    var <variable_name> <type>

Після оголошення змінної певного типу ви можете призначити змінну будь-якому значенню цього типу.

Ви також можете надати початкове значення змінній під час самого оголошення за допомогою

    var <variable_name> <type> = <value>

Якщо ви оголошуєте змінну з початковим значенням, Go виводить тип змінної з типу присвоєного значення. Таким чином, ви можете опустити тип під час оголошення за допомогою синтаксису

    var <variable_name> = <value>

Крім того, ви можете оголосити кілька змінних за допомогою синтаксису

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

Наведена нижче програма в цьому підручнику з Go містить декілька прикладів оголошень змінних Golang

 
package main
import "fmt"

func main() {
    //declaring a integer variable x
    var x int
    x=3 //assigning x the value 3 
    fmt.Println("x:", x) //prints 3
    
    //declaring a integer variable y with value 20 in a single statement and prints it
    var y int=20
    fmt.Println("y:", y)
    
    //declaring a variable z with value 50 and prints it
    //Here type int is not explicitly mentioned 
    var z=50
    fmt.Println("z:", z)
    
    //Multiple variables are assigned in single line- i with an integer and j with a string
    var i, j = 100,"hello"
    fmt.Println("i and j:", i,j)
}

Вихід буде

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

Go Language також надає простий спосіб оголошення змінних зі значенням, пропускаючи ключове слово var

    <variable_name> := <value>

Зверніть увагу, що Ви використовували := замість =. Ви не можете використовувати := лише для призначення значення змінній, яка вже оголошена. := використовується для оголошення та присвоєння значення.

Створіть файл під назвою assign.go із таким кодом

package main
import ("fmt")

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

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

Виконайте go run assign.go, щоб побачити результат як

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

Змінні, оголошені без початкового значення, матимуть 0 для числових типів, false для логічних типів і порожній рядок для рядків

Константи

Постійні змінні - це ті змінні, значення яких не можна змінити після призначення. Константа в мові програмування Go оголошується за допомогою ключового слова «const»

Створіть файл з назвою constant.go і з таким кодом

package main
import ("fmt")

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

Виконайте go run constant.go, щоб побачити результат як

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

Приклади циклу For

Цикли використовуються для багаторазового виконання блоку операторів на основі умови. Більшість мов програмування надають 3 типи циклів – for, while, do while. Але мова програмування Go підтримує лише цикл for.

Синтаксис циклу Golang for такий

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

Initialisation_expression виконується першим (і лише один раз) у циклі Golang for.

Потім оцінюється вираз_valuation_expression, і якщо він істинний, виконується код усередині блоку.

Ідентифікатор iteration_expression виконується, а evaluation_expression обчислюється знову. Якщо це істина, блок операторів виконується знову. Це триватиме, доки evaluation_expression не стане false.

Скопіюйте наведену нижче програму у файл і виконайте її, щоб побачити цифри циклу Golang для друку від 1 до 5

package main
import "fmt"

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

Вихід є

1
2
3
4
5

Якщо ще

If else є умовним оператором. Синакс є

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

Тут умова оцінюється, і якщо вона вірна, оператори_1 будуть виконані, інакше будуть виконані оператори_2.

Ви також можете використовувати оператор if без else. Ви також можете мати ланцюгові оператори if else. Наведені нижче програми пояснять більше про if else.

Виконайте наведену нижче програму. Він перевіряє, чи число x менше 10. Якщо так, виводиться «x менше 10».

package main
import "fmt"

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

Тут, оскільки значення x більше 10, оператор всередині умови блоку if не буде виконано.

Тепер перегляньте наведену нижче програму. У цьому підручнику з мови програмування Go ми маємо блок else, який буде виконано у разі помилки оцінки if.

package main
import "fmt"

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

Ця програма надасть вам результат

x is greater than or equals 10

У цьому посібнику з Go ми побачимо програму з декількома блоками if else (з’єднаними if else). Виконайте наведений нижче приклад Go. Він перевіряє, чи є число менше за 10, між 10-90 чи більше за 90.

package main
import "fmt"

func main() {  
    var x = 100
    if x < 10 {
        //Executes if x is less than 10
        fmt.Println("x is less than 10")
    } else if x >= 10 && x <= 90 {
        //Executes if x >= 10 and x<=90
        fmt.Println("x is between 10 and 90")
    } else {
        //Executes if both above cases fail i.e x>90
        fmt.Println("x is greater than 90")
    }
}

Тут спочатку умова if перевіряє, чи х менше 10, а це ні. Таким чином, він перевіряє наступну умову (інакше, якщо), чи знаходиться вона між 10 і 90, що також є хибним. Таким чином, він виконує блок у розділі else, який дає результат

x is greater than 90

перемикач

Перемикач - ще один умовний оператор. Інструкції Switch обчислюють вираз, і результат порівнюється з набором доступних значень (випадків). Коли знайдено збіг, виконуються оператори, пов’язані з цим збігом (регістром). Якщо відповідності не знайдено, нічого не буде виконано. Ви також можете додати регістр за замовчуванням для перемикання, який буде виконано, якщо не знайдено інших збігів. Синтаксис перемикача такий

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

Тут значення виразу порівнюється зі значеннями в кожному випадку. Коли збіг знайдено, виконуються оператори, пов’язані з цим випадком. Якщо збігів не знайдено, виконуються оператори в розділі за замовчуванням.

Виконайте наведену нижче програму

package main
import "fmt"

func main() {  
    a,b := 2,1
    switch a+b {
    case 1:
        fmt.Println("Sum is 1")
    case 2:
        fmt.Println("Sum is 2")
    case 3:
        fmt.Println("Sum is 3")
    default:
        fmt.Println("Printing default")
    }
}

Ви отримаєте результат як

Sum is 3		

Змініть значення a і b на 3, і результат буде таким

Printing default

Ви також можете мати кілька значень в регістрі, розділяючи їх комою.

Масиви

Масив представляє фіксований розмір, іменовану послідовністю елементів одного типу. Ви не можете мати масив, який містить цілі числа та символи. Ви не можете змінити розмір масиву після визначення розміру.

Синтаксис для оголошення масиву такий

var arrayname [size] type

Кожному елементу масиву можна присвоїти значення за допомогою синтаксису

arrayname [index] = value

Індекс масиву починається з від 0 до розміру-1.

Ви можете присвоювати значення елементам масиву під час оголошення за допомогою синтаксису

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

Ви також можете ігнорувати параметр size під час оголошення масиву зі значеннями, замінивши size на ... а компілятор знайде довжину за кількістю значень. Синтаксис є

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

Ви можете знайти довжину масиву за допомогою синтаксису

len(arrayname)

Виконайте наведений нижче приклад Go, щоб зрозуміти масив

package main
import "fmt"

func main() {  
    var numbers [3] string //Declaring a string array of size 3 and adding elements 
    numbers[0] = "One"
    numbers[1] = "Two"
    numbers[2] = "Three"
    fmt.Println(numbers[1]) //prints Two
    fmt.Println(len(numbers)) //prints 3
    fmt.Println(numbers) // prints [One Two Three]

    directions := [...] int {1,2,3,4,5} // creating an integer array and the size of the array is defined by the number of elements 
    fmt.Println(directions) //prints [1 2 3 4 5]
    fmt.Println(len(directions)) //prints 5

    //Executing the below commented statement prints invalid array index 5 (out of bounds for 5-element array)
    //fmt.Println(directions[5]) 
}

Вихід

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

Golang Slice і функція додавання

Зріз — це частина або сегмент масиву. Або це вигляд або частковий вигляд основного масиву, на який він вказує. Ви можете отримати доступ до елементів фрагмента за допомогою імені фрагмента та номера індексу так само, як ви робите це в масиві. Ви не можете змінити довжину масиву, але ви можете змінити розмір фрагмента.

Вміст фрагмента фактично є покажчиками на елементи масиву. Це означає якщо ви зміните будь-який елемент у фрагменті, це також вплине на базовий вміст масиву.

Синтаксис створення фрагмента такий

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

Це створить фрагмент під назвою slice_name з масиву під назвою array_name з елементами від початку до кінця-1.

Тепер у цьому підручнику Golang ми виконаємо наведену нижче програму. Програма створить фрагмент із масиву та роздрукує його. Крім того, ви бачите, що зміна вмісту фрагмента призведе до зміни фактичного масиву.

package main
import "fmt"

func main() {  
    // declaring array
    a := [5] string {"one", "two", "three", "four", "five"}
    fmt.Println("Array after creation:",a)

    var b [] string = a[1:4] //created a slice named b
    fmt.Println("Slice after creation:",b)

    b[0]="changed" // changed the slice data
    fmt.Println("Slice after modifying:",b)
    fmt.Println("Array after slice modification:",a)
}

Це надрукує результат як

Array after creation: [one two three four five]
Slice after creation: [two three four]
Slice after modifying: [changed three four]
Array after slice modification: [one changed three four five]

Існують певні функції, як-от Golang len, Golang append, які можна застосувати до фрагментів

len(slice_name) – повертає довжину фрагмента

append(slice_name, value_1, value_2) – Golang append використовується для додавання value_1 і value_2 до існуючого фрагмента.

append(slice_nale1,slice_name2…) – додає slice_name2 до slice_name1

Виконайте наступну програму.

package main
import "fmt"

func main() {  
	a := [5] string {"1","2","3","4","5"}
	slice_a := a[1:3]
	b := [5] string {"one","two","three","four","five"}
	slice_b := b[1:3]

    fmt.Println("Slice_a:", slice_a)
    fmt.Println("Slice_b:", slice_b)
    fmt.Println("Length of slice_a:", len(slice_a))
    fmt.Println("Length of slice_b:", len(slice_b))

    slice_a = append(slice_a,slice_b...) // appending slice
    fmt.Println("New Slice_a after appending slice_b :", slice_a)
    
    slice_a = append(slice_a,"text1") // appending value
    fmt.Println("New Slice_a after appending text1 :", slice_a)
}

Вихід буде

Slice_a: [2 3]
Slice_b: [two three]
Length of slice_a: 2
Length of slice_b: 2
New Slice_a after appending slice_b : [2 3 two three]
New Slice_a after appending text1 : [2 3 two three text1]

Програма спочатку створює 2 зрізи та друкує їх довжину. Потім він додав один фрагмент до іншого, а потім додав рядок до отриманого фрагмента.

Функції

Функція являє собою блок операторів, який виконує певне завдання. Оголошення функції повідомляє нам назву функції, тип повернення та вхідні параметри. Визначення функції представляє код, що міститься у функції. Синтаксис для оголошення функції такий

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

Параметри та типи повернення є необов’язковими. Крім того, ви можете повернути кілька значень із функції.

Тепер у цьому підручнику Golang розглянемо наступний приклад Golang. Тут функція під назвою calc приймає 2 числа та виконує додавання та віднімання та повертає обидва значення.

package main
import "fmt"

//calc is the function name which accepts two integers num1 and num2
//(int, int) says that the function returns two values, both of integer type.
func calc(num1 int, num2 int)(int, int) {  
    sum := num1 + num2
    diff := num1 - num2
    return sum, diff
}

func main() {  
    x,y := 15,10

    //calls the function calc with x and y an d gets sum, diff as output
    sum, diff := calc(x,y) 
    fmt.Println("Sum",sum)
    fmt.Println("Diff",diff) 
}

Вихід буде

Sum 25
Diff 5

Пакети

Для організації коду використовуються пакети. У великому проекті неможливо написати код в одному файлі. Мова програмування Go дозволяє організовувати код за різними пакетами. Це підвищує читабельність коду та можливість повторного використання. Виконувана програма Go повинна містити пакет під назвою main, і виконання програми починається з функції під назвою main. Ви можете імпортувати інші пакети в нашу програму за допомогою синтаксису

import package_name

У цьому підручнику Golang ми побачимо та обговоримо, як створювати та використовувати пакунки в наступному прикладі Golang.

Крок 1) Створіть файл під назвою package_example.go і додайте наведений нижче код

package main
import "fmt"
//the package to be created
import "calculation"

func main() {  
	x,y := 15,10
	//the package will have function Do_add()
sum := calculation.Do_add(x,y)
fmt.Println("Sum",sum) 
}

У наведеній вище програмі fmt — це пакет, який мова програмування Go надає нам переважно для цілей введення/виведення. Крім того, ви можете побачити пакет під назвою Calculation. Всередині main() ви можете побачити суму кроків := обчислення.Do_add(x,y). Це означає, що ви викликаєте функцію Do_add з обчислення пакета.

Крок 2) По-перше, вам слід створити обчислення пакета в папці з такою самою назвою в папці src у go. Встановлений шлях go можна знайти за змінною PATH.

Для Mac знайдіть шлях, виконавши echo $PATH

Отже, шлях /usr/local/go

Для Windows знайдіть шлях, виконавши echo %GOROOT%

Тут шлях C:\Go\

Крок 3) Перейдіть до папки src (/usr/local/go/src для Mac і C:\Go\src для Windows). Тепер із коду ім’я пакета — це обчислення. Go вимагає, щоб пакет було розміщено в каталозі з такою самою назвою в каталозі src. Створіть каталог під назвою «розрахунки» в папці src.

Крок 4) Створіть файл під назвою calc.go (Ви можете дати будь-яку назву, але назва пакета в коді має значення. Тут має бути обчислення) у каталозі обчислень і додайте наведений нижче код

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

Крок 5) Виконайте команду go install з каталогу обчислень, яка скомпілює calc.go.

Крок 6) Тепер поверніться до package_example.go і запустіть go run package_example.go. Результатом буде сума 25.

Зверніть увагу, що назва функції Do_add починається з великої літери. Це тому, що в Go, якщо назва функції починається з великої літери, це означає, що інші програми можуть бачити (доступ) до неї, інакше інші програми не можуть отримати до неї доступ. Якби ім'я функції було do_add, тоді ви отримали б помилку

не може посилатися на неекспортоване ім'яkalculation.calc..

Відкладення та укладання відкладень

Інструкції defer використовуються для відкладення виконання виклику функції, доки функція, яка містить інструкцію defer, не завершить виконання.

Давайте дізнаємося це на прикладі:

package main
import "fmt"

func sample() {  
    fmt.Println("Inside the sample()")
}
func main() {  
    //sample() will be invoked only after executing the statements of main()
    defer sample()
    fmt.Println("Inside the main()")
}

Вихід буде

Inside the main()
Inside the sample()

Тут виконання sample() відкладено, доки не завершиться виконання охоплюючої функції(main()).

У стекуванні defer використовується кілька операторів defer. Припустімо, що у функції є кілька операторів defer. Go поміщає всі відкладені виклики функцій у стек, і коли функція-охоплювач повертається, функції зі стеком виконуються в Порядок «останній прийшов, перший вийшов» (LIFO). Ви можете побачити це на прикладі нижче.

Виконайте наведений нижче код

package main
import "fmt"

func display(a int) {  
    fmt.Println(a)
}
func main() {  
    defer display(1)
    defer display(2)
    defer display(3)
    fmt.Println(4)
}

Вихід буде

4
3
2
1			

Тут спочатку виконується код у main(), а потім відкладені виклики функцій виконуються у зворотному порядку, тобто 4, 3,2,1.

покажчики

Перш ніж пояснювати покажчики, давайте спочатку обговоримо оператор &. Оператор «&» використовується для отримання адреси змінної. Це означає, що '&a' виведе адресу пам'яті змінної a.

У цьому підручнику Golang ми виконаємо наведену нижче програму, щоб відобразити значення змінної та адресу цієї змінної

package main
import "fmt"

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

Результат буде

Address: 0xc000078008
Value: 20

Змінна-покажчик зберігає адресу пам'яті іншої змінної. Ви можете визначити покажчик за допомогою синтаксису

	var variable_name *type

Зірочка (*) означає, що змінна є покажчиком. Ви зрозумієте більше, виконавши наведену нижче програму

package main
import "fmt"

func main() {
	//Create an integer variable a with value 20
	a := 20
	
	//Create a pointer variable b and assigned the address of a
	var b *int = &a

	//print address of a(&a) and value of a  
	fmt.Println("Address of a:",&a)
	fmt.Println("Value of a:",a)

	//print b which contains the memory address of a i.e. &a
	fmt.Println("Address of pointer b:",b)

	//*b prints the value in memory address which b contains i.e. the value of a
	fmt.Println("Value of pointer b",*b)

	//increment the value of variable a using the variable b
	*b = *b+1

	//prints the new value using a and *b
	fmt.Println("Value of pointer b",*b)
	fmt.Println("Value of a:",a)}

Вихід буде

Address of a: 0x416020
Value of a: 20
Address of pointer b: 0x416020
Value of pointer b 20
Value of pointer b 21
Value of a: 21

Структури

Структура — це визначений користувачем тип даних, який містить ще один елемент того самого або іншого типу.

Використання структури — це двоетапний процес.

Спочатку створіть (оголосите) тип структури

По-друге, створіть змінні цього типу для зберігання значень.

Структури в основному використовуються, коли потрібно зберігати пов’язані дані разом.

Розглянемо частину інформації про співробітника, яка містить ім’я, вік та адресу. Ви можете впоратися з цим двома способами

Створіть 3 масиви – один зберігає імена співробітників, один зберігає вік і третій зберігає вік.

Оголошіть тип структури з трьома полями: ім’я, адреса та вік. Створіть масив такого структурного типу, де кожен елемент є структурним об’єктом із назвою, адресою та віком.

Перший спосіб не ефективний. У таких сценаріях структури зручніші.

Синтаксис для оголошення структури такий

type structname struct {
   variable_1 variable_1_type
   variable_2 variable_2_type
   variable_n variable_n_type
}

Прикладом оголошення структури є

type emp struct {
    name string
    address string
    age int
}

Тут створюється новий визначений користувачем тип під назвою emp. Тепер ви можете створювати змінні типу emp за допомогою синтаксису

	var variable_name struct_name

Прикладом може служити

var empdata1 emp 

Ви можете встановити значення для empdata1 як

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

Ви також можете створити структурну змінну та призначити значення

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

Тут потрібно зберегти порядок елементів. Raj буде зіставлено з назвою, наступний елемент з адресою, а останній з віком.

Виконайте наведений нижче код

package main
import "fmt"

//declared the structure named emp
type emp struct {
        name string
        address string
        age int
}       

//function which accepts variable of emp type and prints name property
func display(e emp) {
          fmt.Println(e.name)
}

func main() {
// declares a variable, empdata1, of the type emp
var empdata1 emp
//assign values to members of empdata1
empdata1.name = "John"
empdata1.address = "Street-1, London"
empdata1.age = 30

//declares and assign values to variable empdata2 of type emp
empdata2 := emp{"Raj", "Building-1, Paris", 25}

//prints the member name of empdata1 and empdata2 using display function
display(empdata1)
display(empdata2)
}

Вихід

John
Raj

Методи (не функції)

Метод — це функція з аргументом приймача. Archiструктурно це між ключовим словом func і назвою методу. Синтаксис методу такий

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

Давайте перетворимо наведений вище приклад програми на використання методів замість функції.

package main
import "fmt"

//declared the structure named emp
type emp struct {
    name string
    address string
    age int
}

//Declaring a function with receiver of the type emp
func(e emp) display() {
    fmt.Println(e.name)
}

func main() {
    //declaring a variable of type emp
    var empdata1 emp
    
    //Assign values to members
    empdata1.name = "John"
    empdata1.address = "Street-1, Lodon"
    empdata1.age = 30

    //declaring a variable of type emp and assign values to members
    empdata2 := emp {
        "Raj", "Building-1, Paris", 25}

    //Invoking the method using the receiver of the type emp
   // syntax is variable.methodname()
    empdata1.display()
    empdata2.display()
}

Go не є об’єктно-орієнтованою мовою і не має поняття класу. Методи дають відчуття того, що ви робите в об’єктно-орієнтованих програмах, де функції класу викликаються за допомогою синтаксису objectname.functionname()

Паралелізм

Go підтримує одночасне виконання завдань. Це означає, що Go може виконувати кілька завдань одночасно. Воно відрізняється від концепції паралелізму. У паралелізмі завдання розбивається на невеликі підзадачі, які виконуються паралельно. Але в паралельному режимі кілька завдань виконуються одночасно. Одночасність досягається в Go за допомогою Goroutines і Channels.

Goroutines

Goroutine — це функція, яка може працювати одночасно з іншими функціями. Зазвичай, коли функція викликається, керування передається до викликаної функції, а після завершення її виконання керування повертається до функції, що викликає. Потім функція, що викликає, продовжує своє виконання. Викликаюча функція чекає, поки викликана функція завершить виконання, перш ніж продовжити виконання решти операторів.

Але у випадку goroutine функція, що викликає, не чекатиме завершення виконання викликаної функції. Він продовжить виконуватися з наступними операторами. У програмі можна мати декілька gor-програм.

Крім того, головна програма завершить роботу, коли завершить виконання своїх інструкцій, і вона не чекатиме завершення викликаних підпрограмм.

Goroutine викликається за допомогою ключового слова go, за яким слідує виклик функції.

Приклад

go add(x,y)

Ви зрозумієте goroutines з наведеними нижче прикладами Golang. Виконайте наведену нижче програму

package main
import "fmt"
    
func display() {
	for i:=0; i<5; i++ {
		fmt.Println("In display")
	}
}

func main() {
	//invoking the goroutine display()
	go display()
	//The main() continues without waiting for display()
	for i:=0; i<5; i++ {
		fmt.Println("In main")
	}
}

Вихід буде

In main
In main
In main
In main
In main

Тут головна програма завершила виконання ще до того, як запустилася горутина. Display() — це підпрограма, яка викликається за допомогою синтаксису

go function_name(parameter list)

У наведеному вище коді main() не чекає завершення display(), а main() завершив своє виконання до того, як display() виконає свій код. Отже, оператор print у display() не друкувався.

Тепер ми змінюємо програму, щоб також друкувати оператори з display(). Ми додаємо затримку в 2 секунди в циклі for main() і затримку в 1 секунду в циклі for display().

package main
import "fmt"
import "time"
    
func display() {
	for i:=0; i<5; i++ {
		time.Sleep(1 * time.Second)
		fmt.Println("In display")
	}
}

func main() {
	//invoking the goroutine display()
	go display()
	for i:=0; i<5; i++ {
		time.Sleep(2 * time.Second)
		fmt.Println("In main")
	}
}

Результат буде дещо схожий на

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

Тут ви можете побачити, що обидва цикли виконуються з перекриттям через одночасне виконання.

Канали

Канали — це спосіб спілкування функцій між собою. Його можна розглядати як засіб, де одна підпрограма розміщує дані та отримує доступ до іншої підпрограми на сервері Golang.

Канал можна оголосити за допомогою синтаксису

channel_variable := make(chan datatype)

приклад:

	ch := make(chan int)

Ви можете надсилати дані в канал за допомогою синтаксису

channel_variable <- variable_name

Приклад

    ch <- x

Ви можете отримати дані з каналу за допомогою синтаксису

    variable_name := <- channel_variable

Приклад

   y := <- ch

У наведених вище прикладах goroutine мовою Go ви бачили, що основна програма не чекає goroutine. Але це не той випадок, коли залучені канали. Припустімо, якщо підпрограма goroutine надсилає дані в канал, main() чекатиме оператора, який отримує дані каналу, доки не отримає дані.

Ви побачите це в прикладах мови Go нижче. Спочатку напишіть звичайну goroutine і подивіться поведінку. Потім змініть програму, щоб використовувати канали, і подивіться поведінку.

Виконайте наведену нижче програму

package main
import "fmt"
import "time"
    
func display() {
	time.Sleep(5 * time.Second)
	fmt.Println("Inside display()")
}

func main() {
	go display()
	fmt.Println("Inside main()")
}

Вихід буде

Inside main()

Основний() завершив виконання та завершив роботу перед виконанням goroutine. Отже, друк всередині display() не було виконано.

Тепер змініть наведену вище програму, щоб використовувати канали та переглядати поведінку.

package main
import "fmt"
import "time"
    
func display(ch chan int) {
	time.Sleep(5 * time.Second)
	fmt.Println("Inside display()")
	ch <- 1234
}

func main() {
	ch := make(chan int) 
	go display(ch)
	x := <-ch
	fmt.Println("Inside main()")
	fmt.Println("Printing x in main() after taking from channel:",x)
}

Вихід буде

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

Ось що відбувається, коли main() досягає x := <-ch чекатиме даних на каналі ch. Display() чекає 5 секунд, а потім надсилає дані в канал ch. Основний() після отримання даних з каналу розблоковується і продовжує своє виконання.

Відправник, який надсилає дані в канал, може повідомити одержувачів, що дані більше не додаватимуться в канал, закривши канал. Це в основному використовується, коли ви використовуєте цикл для надсилання даних до каналу. Канал можна закрити за допомогою

close(channel_name)

А на стороні приймача можна перевірити, чи закритий канал за допомогою додаткової змінної, під час отримання даних із каналу за допомогою

variable_name, status := <- channel_variable

Якщо статус True, це означає, що ви отримали дані з каналу. Якщо false, це означає, що ви намагаєтеся читати із закритого каналу

Ви також можете використовувати канали для зв’язку між підпрограмами. Необхідно використовувати 2 goroutines – одна надсилає дані в канал, а інша отримує дані з каналу. Дивіться наведену нижче програму

package main
import "fmt"
import "time"

//This subroutine pushes numbers 0 to 9 to the channel and closes the channel
func add_to_channel(ch chan int) {	
	fmt.Println("Send data")
	for i:=0; i<10; i++ {
		ch <- i //pushing data to channel
	}
	close(ch) //closing the channel

}

//This subroutine fetches data from the channel and prints it.
func fetch_from_channel(ch chan int) {
	fmt.Println("Read data")
	for {
		//fetch data from channel
x, flag := <- ch

		//flag is true if data is received from the channel
//flag is false when the channel is closed
if flag == true {
			fmt.Println(x)
		}else{
			fmt.Println("Empty channel")
			break	
		}	
	}
}

func main() {
	//creating a channel variable to transport integer values
	ch := make(chan int)

	//invoking the subroutines to add and fetch from the channel
	//These routines execute simultaneously
	go add_to_channel(ch)
	go fetch_from_channel(ch)

	//delay is to prevent the exiting of main() before goroutines finish
	time.Sleep(5 * time.Second)
	fmt.Println("Inside main()")
}

Тут є 2 підпрограми, одна надсилає дані в канал, а інша друкує дані в канал. Функція add_to_channel додає числа від 0 до 9 і закриває канал. Одночасно функція fetch_from_channel очікує на

x, flag := <- ch і як тільки дані стають доступними, вони друкуються. Він виходить, коли прапор стає хибним, що означає, що канал закрито.

Очікування в main() дається, щоб запобігти виходу main(), доки горограми не завершать виконання.

Виконайте код і перегляньте результат як

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

Виберіть

Select можна розглядати як оператор switch, який працює на каналах. Тут оператори case будуть операцією каналу. Зазвичай, кожен оператор case буде спробою прочитати з каналу. Коли будь-який із випадків готовий (канал зчитується), тоді виконується оператор, пов’язаний із цим випадком. Якщо готово кілька кейсів, він вибере випадковий. Ви можете мати випадок за замовчуванням, який виконується, якщо жоден із випадків не готовий.

Давайте подивимось наведений нижче код

package main
import "fmt"
import "time"

//push data to channel with a 4 second delay
func data1(ch chan string) {  
    time.Sleep(4 * time.Second)
    ch <- "from data1()"
}

//push data to channel with a 2 second delay
func data2(ch chan string) {  
    time.Sleep(2 * time.Second)
    ch <- "from data2()"
}

func main() {
    //creating channel variables for transporting string values
    chan1 := make(chan string)
    chan2 := make(chan string)
    
    //invoking the subroutines with channel variables
    go data1(chan1)
    go data2(chan2)
    
    //Both case statements wait for data in the chan1 or chan2.
    //chan2 gets data first since the delay is only 2 sec in data2().
    //So the second case will execute and exits the select block
    select {
    case x := <-chan1:
        fmt.Println(x)
    case y := <-chan2:
        fmt.Println(y)
    }
}

Виконання наведеної вище програми дасть результат:

from data2()

Тут оператор select очікує, поки дані стануть доступними в будь-якому з каналів. Функція data2() додає дані в канал після 2-секундного сплячого режиму, що призведе до виконання другого випадку.

Додайте регістр за замовчуванням до select у тій же програмі та подивіться результат. Тут, досягнувши вибраного блоку, якщо в жодному випадку немає готових даних на каналі, він виконає блок за замовчуванням, не чекаючи, поки дані стануть доступними на будь-якому каналі.

package main
import "fmt"
import "time"

//push data to channel with a 4 second delay
func data1(ch chan string) {  
    time.Sleep(4 * time.Second)
    ch <- "from data1()"
}

//push data to channel with a 2 second delay
func data2(ch chan string) {  
    time.Sleep(2 * time.Second)
    ch <- "from data2()"
}

func main() {
    //creating channel variables for transporting string values  
    chan1 := make(chan string)
    chan2 := make(chan string)
    
    //invoking the subroutines with channel variables
    go data1(chan1)
    go data2(chan2)

    //Both case statements check for data in chan1 or chan2.
    //But data is not available (both routines have a delay of 2 and 4 sec)
    //So the default block will be executed without waiting for data in channels.
    select {
    case x := <-chan1:
        fmt.Println(x)
    case y := <-chan2:
        fmt.Println(y)
    default:
    	fmt.Println("Default case executed")
    }
}

Ця програма дасть результат:

Default case executed			

Це пов’язано з тим, що після досягнення блоку вибору жоден канал не мав даних для читання. Отже, випадок за замовчуванням виконується.

Mutex

Mutex — це скорочена форма взаємного виключення. Mutex використовується, коли ви не хочете дозволяти доступ до ресурсу декільком підпрограмам одночасно. Mutex має 2 методи – Lock і Unlock. Mutex міститься в пакеті синхронізації. Отже, вам потрібно імпортувати пакет синхронізації. Інструкції, які повинні виконуватися взаємовиключно, можна розмістити всередині mutex.Lock() і mutex.Unlock().

Давайте вивчимо м’ютекс на прикладі підрахунку кількості виконання циклу. У цій програмі ми очікуємо, що підпрограма виконає цикл 10 разів, і підрахунок зберігається в сумі. Ви викликаєте цю процедуру 3 рази, тому загальна кількість має бути 30. Кількість зберігається в глобальній змінній count.

По-перше, ви запускаєте програму без м'ютексу

package main
import "fmt"
import "time"
import "strconv"
import "math/rand"
//declare count variable, which is accessed by all the routine instances
var count = 0

//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
	//loop incrementing the count by 10
	for i := 0; i < 10; i++ {
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		temp := count
		temp++
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		count = temp
	}
	fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}

func main() {
	//loop calling the process() 3 times
	for i := 1; i < 4; i++ {
		go process(i)
	}

	//delay to wait for the routines to complete
	time.Sleep(25 * time.Second)
	fmt.Println("Final Count:", count)
}

Подивіться результат

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

Результат може бути іншим, коли ви його виконуєте, але кінцевий результат не буде 30.

Ось що відбувається: 3 gor-програми намагаються збільшити кількість циклів, збережену в змінній count. Припустімо, що в даний момент кількість дорівнює 5, а goroutine1 збирається збільшити кількість до 6. Основні кроки включають

Копіювати кількість до темп

Збільшити темп

Зберігайте температуру назад для підрахунку

Припустимо, незабаром після виконання кроку 3 goroutine1; інша goroutine може мати старе значення, скажімо, 3 виконує описані вище дії та зберігає 4 назад, що є неправильним. Цьому можна запобігти, використовуючи м'ютекс, який змушує інші підпрограми чекати, коли одна підпрограма вже використовує змінну.

Тепер ви запустите програму з м'ютексом. Тут згадані вище 3 кроки виконуються у м’ютексі.

package main
import "fmt"
import "time"
import "sync"
import "strconv"
import "math/rand"

//declare a mutex instance
var mu sync.Mutex

//declare count variable, which is accessed by all the routine instances
var count = 0

//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
	//loop incrementing the count by 10
	for i := 0; i < 10; i++ {
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		//lock starts here
		mu.Lock()
		temp := count
		temp++
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		count = temp
		//lock ends here
		mu.Unlock()
	}
	fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}

func main() {
	//loop calling the process() 3 times
	for i := 1; i < 4; i++ {
		go process(i)
	}

	//delay to wait for the routines to complete
	time.Sleep(25 * time.Second)
	fmt.Println("Final Count:", count)
}

Тепер вихід буде

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

Тут ми отримуємо очікуваний результат як кінцевий результат. Оскільки оператори читання, збільшення та зворотного запису лічильника виконуються у м’ютексі.

Помилка обробки

Помилки — це ненормальні умови, такі як закриття файлу, який не відкривається, відкриття файлу, який не існує, тощо. Функції зазвичай повертають помилки як останнє значення, що повертається.

Наведений нижче приклад пояснює більше про помилку.

package main
import "fmt"
import "os"

//function accepts a filename and tries to open it.
func fileopen(name string) {
    f, er := os.Open(name)

    //er will be nil if the file exists else it returns an error object  
    if er != nil {
        fmt.Println(er)
        return
    }else{
    	fmt.Println("file opened", f.Name())
    }
}

func main() {  
    fileopen("invalid.txt")
}

Вихідний сигнал буде:

open /invalid.txt: no such file or directory

Тут ми спробували відкрити неіснуючий файл, і він повернув помилку до змінної er. Якщо файл дійсний, то помилка буде нульовою

Користувацькі помилки

Використовуючи цю функцію, ви можете створювати власні помилки. Це робиться за допомогою New() пакета помилок. Ми перепишемо наведену вище програму, щоб використовувати власні помилки.

Запустіть наведену нижче програму

package main
import "fmt"
import "os"
import "errors"

//function accepts a filename and tries to open it.
func fileopen(name string) (string, error) {
    f, er := os.Open(name)

    //er will be nil if the file exists else it returns an error object  
    if er != nil {
        //created a new error object and returns it  
        return "", errors.New("Custom error message: File name is wrong")
    }else{
    	return f.Name(),nil
    }
}

func main() {  
    //receives custom error or nil after trying to open the file
    filename, error := fileopen("invalid.txt")
    if error != nil {
        fmt.Println(error)
    }else{
    	fmt.Println("file opened", filename)
    }  
}

Вихідний сигнал буде:

Custom error message:File name is wrong

Тут area() повертає площу квадрата. Якщо введене значення менше 1, area() повертає повідомлення про помилку.

Читання файлів

Файли використовуються для зберігання даних. Go дозволяє нам читати дані з файлів

Спочатку створіть файл data.txt у поточному каталозі з наведеним нижче вмістом.

Line one
Line two
Line three

Тепер запустіть наведену нижче програму, щоб побачити, як вона виводить вміст усього файлу

package main
import "fmt"
import "io/ioutil"

func main() {  
    data, err := ioutil.ReadFile("data.txt")
    if err != nil {
        fmt.Println("File reading error", err)
        return
    }
    fmt.Println("Contents of file:", string(data))
}

Тут дані, err := ioutil.ReadFile(“data.txt”) читає дані та повертає послідовність байтів. Під час друку він перетворюється на рядковий формат.

Запис файлів

Ви побачите це за допомогою програми

package main
import "fmt"
import "os"

func main() {  
    f, err := os.Create("file1.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    l, err := f.WriteString("Write Line one")
    if err != nil {
        fmt.Println(err)
        f.Close()
        return
    }
    fmt.Println(l, "bytes written")
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        return
    }
}

Тут створюється файл test.txt. Якщо файл уже існує, вміст файлу скорочується. Writeline() використовується для запису вмісту у файл. Після цього ви закрили файл за допомогою Close().

Чит лист

У цьому посібнику з Go ми розглянули,

Тема Описи конструкції синтаксис
Основні типи Числовий, рядковий, логічний
Змінні Оголошуйте та присвоюйте значення змінним var variable_name тип
змінна назва_змінної тип = значення
змінна назва_змінної1, назва_змінної2 = значення1, значення2
назва_змінної := значення
Константи Змінні, значення яких не можна змінити після призначення const змінна = значення
Для петлі Виконувати оператори в циклі. для ініціалізації_виразу; оцінка_виразу; iteration_expression{
// один або декілька операторів
}
Якщо ще Це умовне твердження якщо умова{
// statements_1
} Ще {
// statements_2
}
перемикання Умовне твердження з кількома відмінками вираз перемикання {
case value_1:
заяви_1
case value_2:
заяви_2
case value_n:
заяви_п
за замовчуванням:
statements_default
}
масив Послідовність елементів одного типу з фіксованим розміром arrayname := [розмір] тип {value_0,value_1,…,value_size-1}
Скибочка Частина або сегмент масиву змінна назва_фрагмента [] тип = назва_масиву[початок:кінець]
Функції Блок операторів, який виконує певне завдання func назва_функції (тип параметра_1, тип параметра_n) тип_повернення {
//заяви
}
Пакети Використовуються для організації коду. Підвищує читабельність коду та можливість повторного використання Імпорт пакет_назва
Відкласти Відкладає виконання функції до завершення виконання функції, що містить її defer function_name(parameter_list)
покажчики Зберігає адресу пам'яті іншої змінної. змінна ім'я_змінної *тип
Структура Визначений користувачем тип даних, який містить ще один елемент того самого або іншого типу введіть structname struct {
змінна_1 тип_змінної_1
змінна_2 тип_змінної_2
змінна_n змінна_n_тип
}
Методи Метод — це функція з аргументом приймача func (змінна variabletype) methodName(parameter_list) {
}
Goroutine Функція, яка може працювати одночасно з іншими функціями. go function_name(parameter_list)
Канал Спосіб спілкування функцій між собою. Носій, на який одна підпрограма розміщує дані та до якого отримує доступ інша підпрограма. Заявити:
ch := make(chan int)
Надіслати дані на канал:
змінна_каналу <- назва_змінної
Отримати з каналу:
назва_змінної := <- змінна_каналу
Виберіть Оператор Switch, який працює на каналах. Інструкції case будуть операцією каналу. Коли будь-який із каналів готовий із даними, тоді виконується оператор, пов’язаний із цим випадком виберіть {
case x := <-chan1:
fmt.Println(x)
case y := <-chan2:
fmt.Println(y)
}
Mutex Mutex використовується, коли ви не хочете дозволяти доступ до ресурсу декільком підпрограмам одночасно. Mutex має 2 методи – Lock і Unlock mutex.Lock()
//заяви
mutex.Unlock().
Читання файлів Читає дані та повертає послідовність байтів. Дані, помилка := ioutil.ReadFile(ім'я файлу)
Записати файл Записує дані у файл l, err := f.WriteString(text_to_write)