1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
|
---
language: Go
filename: learngo-ru.go
contributors:
- ["Sonia Keys", "https://github.com/soniakeys"]
- ["Christopher Bess", "https://github.com/cbess"]
- ["Jesse Johnson", "https://github.com/holocronweaver"]
- ["Quint Guvernator", "https://github.com/qguv"]
translators:
- ["Artem Medeusheyev", "https://github.com/armed"]
- ["Valery Cherepanov", "https://github.com/qumeric"]
lang: ru-ru
---
Go - это язык общего назначения, целью которого является удобство, простота,
конкурентность. Это не тренд в компьютерных науках, а новейший и быстрый
способ решать насущные проблемы.
Концепции Go схожи с другими императивными статически типизированными языками.
Быстро компилируется и быстро исполняется, имеет лёгкие в понимании конструкции
для создания масштабируемых и многопоточных программ.
Может похвастаться отличной стандартной библиотекой и большим комьюнити, полным
энтузиастов.
```go
// Однострочный комментарий
/* Многострочный
комментарий */
// Ключевое слово package присутствует в начале каждого файла.
// main это специальное имя, обозначающее исполняемый файл, нежели библиотеку.
package main
// Import предназначен для указания зависимостей этого файла.
import (
"fmt" // Пакет в стандартной библиотеке Go
"io/ioutil" // Реализация функций ввод/вывода.
"net/http" // Да, это веб-сервер!
"strconv" // Конвертирование типов в строки и обратно
m "math" // Импортировать math под локальным именем m.
)
// Объявление функции. Main это специальная функция, служащая точкой входа для
// исполняемой программы. Нравится вам или нет, но Go использует фигурные
// скобки.
func main() {
// Println выводит строку в stdout.
// Данная функция находится в пакете fmt.
fmt.Println("Hello world!")
// Вызов другой функции из текущего пакета.
beyondHello()
}
// Функции содержат входные параметры в круглых скобках.
// Пустые скобки все равно обязательны, даже если параметров нет.
func beyondHello() {
var x int // Переменные должны быть объявлены до их использования.
x = 3 // Присвоение значения переменной.
// Краткое определение := позволяет объявить переменную с автоматической
// подстановкой типа из значения.
y := 4
sum, prod := learnMultiple(x, y) // Функция возвращает два значения.
fmt.Println("sum:", sum, "prod:", prod) // Простой вывод.
learnTypes() // < y minutes, learn more!
}
// Функция, имеющая входные параметры и возвращающая несколько значений.
func learnMultiple(x, y int) (sum, prod int) {
return x + y, x * y // Возврат двух значений.
}
// Некоторые встроенные типы и литералы.
func learnTypes() {
// Краткое определение переменной говорит само за себя.
s := "Learn Go!" // Тип string.
s2 := `"Чистый" строковой литерал
может содержать переносы строк` // Тоже тип данных string
// Символ не из ASCII. Исходный код Go в кодировке UTF-8.
g := 'Σ' // тип rune, это алиас для типа int32, содержит символ юникода.
f := 3.14159 // float64, 64-х битное число с плавающей точкой (IEEE-754).
c := 3 + 4i // complex128, внутри себя содержит два float64.
// Синтаксис var с инициализациями.
var u uint = 7 // Беззнаковое, но размер зависит от реализации, как и у int.
var pi float32 = 22. / 7
// Синтаксис приведения типа с кратким определением
n := byte('\n') // byte – это алиас для uint8.
// Массивы имеют фиксированный размер на момент компиляции.
var a4 [4]int // массив из 4-х int, инициализирован нулями.
a3 := [...]int{3, 1, 5} // массив из 3-х int, ручная инициализация.
// Слайсы (slices) имеют динамическую длину. И массивы, и слайсы имеют свои
// преимущества, но слайсы используются гораздо чаще.
s3 := []int{4, 5, 9} // Сравните с a3, тут нет троеточия.
s4 := make([]int, 4) // Выделение памяти для слайса из 4-х int (нули).
var d2 [][]float64 // Только объявление, память не выделяется.
bs := []byte("a slice") // Синтаксис приведения типов.
p, q := learnMemory() // Объявление p и q как указателей на int.
fmt.Println(*p, *q) // * извлекает указатель. Печатает два int-а.
// Map, также как и словарь или хеш из некоторых других языков, является
// ассоциативным массивом с динамически изменяемым размером.
m := map[string]int{"three": 3, "four": 4}
m["one"] = 1
delete(m, "three") // Встроенная функция, удаляет элемент из map-а.
// Неиспользуемые переменные в Go являются ошибкой.
// Нижнее подчёркивание позволяет игнорировать такие переменные.
_, _, _, _, _, _, _, _, _ = s2, g, f, u, pi, n, a3, s4, bs
// Вывод считается использованием переменной.
fmt.Println(s, c, a4, s3, d2, m)
learnFlowControl() // Идем дальше.
}
// У Go есть полноценный сборщик мусора. В нем есть указатели, но нет арифметики
// указателей. Вы можете допустить ошибку с указателем на nil, но не с
// инкрементацией указателя.
func learnMemory() (p, q *int) {
// Именованные возвращаемые значения p и q являются указателями на int.
p = new(int) // Встроенная функция new выделяет память.
// Выделенный int проинициализирован нулём, p больше не содержит nil.
s := make([]int, 20) // Выделение единого блока памяти под 20 int-ов.
s[3] = 7 // Присвоить значение одному из них.
r := -2 // Определить ещё одну локальную переменную.
return &s[3], &r // Амперсанд(&) обозначает получение адреса переменной.
}
func expensiveComputation() float64 {
return m.Exp(10)
}
func learnFlowControl() {
// If-ы всегда требуют наличие фигурных скобок, но не круглых.
if true {
fmt.Println("told ya")
}
// Форматирование кода стандартизировано утилитой "go fmt".
if false {
// Будущего нет.
} else {
// Жизнь прекрасна.
}
// Используйте switch вместо нескольких if-else.
x := 42.0
switch x {
case 0:
case 1:
case 42:
// Case-ы в Go не "проваливаются" (неявный break).
case 43:
// Не выполнится.
}
// For, как и if не требует круглых скобок
// Переменные, объявленные в for и if являются локальными.
for x := 0; x < 3; x++ { // ++ – это операция.
fmt.Println("итерация", x)
}
// Здесь x == 42.
// For – это единственный цикл в Go, но у него есть альтернативные формы.
for { // Бесконечный цикл.
break // Не такой уж и бесконечный.
continue // Не выполнится.
}
// Как и в for, := в if-е означает объявление и присвоение значения y,
// проверка y > x происходит после.
if y := expensiveComputation(); y > x {
x = y
}
// Функции являются замыканиями.
xBig := func() bool {
return x > 10000 // Ссылается на x, объявленный выше switch.
}
fmt.Println("xBig:", xBig()) // true (т.к. мы присвоили x = e^10).
x = 1.3e3 // Тут х == 1300
fmt.Println("xBig:", xBig()) // Теперь false.
// Метки, куда же без них, их все любят.
goto love
love:
learnDefer() // Быстрый обзор важного ключевого слова.
learnInterfaces() // О! Интерфейсы, идём далее.
}
func learnDefer() (ok bool) {
// Отложенные(deferred) выражения выполняются сразу перед тем, как функция
// возвратит значение.
defer fmt.Println("deferred statements execute in reverse (LIFO) order.")
defer fmt.Println("\nThis line is being printed first because")
// defer широко используется для закрытия файлов, чтобы закрывающая файл
// функция находилась близко к открывающей.
return true
}
// Объявление Stringer как интерфейса с одним методом, String.
type Stringer interface {
String() string
}
// Объявление pair как структуры с двумя полями x и y типа int.
type pair struct {
x, y int
}
// Объявление метода для типа pair. Теперь pair реализует интерфейс Stringer.
func (p pair) String() string { // p в данном случае называют receiver-ом.
// Sprintf – ещё одна функция из пакета fmt.
// Обращение к полям p через точку.
return fmt.Sprintf("(%d, %d)", p.x, p.y)
}
func learnInterfaces() {
// Синтаксис с фигурными скобками это "литерал структуры". Он возвращает
// проинициализированную структуру, а оператор := присваивает её p.
p := pair{3, 4}
fmt.Println(p.String()) // Вызов метода String у переменной p типа pair.
var i Stringer // Объявление i как типа с интерфейсом Stringer.
i = p // Валидно, т.к. pair реализует Stringer.
// Вызов метода String у i типа Stringer. Вывод такой же, что и выше.
fmt.Println(i.String())
// Функции в пакете fmt сами всегда вызывают метод String у объектов для
// получения строкового представления о них.
fmt.Println(p) // Вывод такой же, что и выше. Println вызывает метод String.
fmt.Println(i) // Вывод такой же, что и выше.
learnVariadicParams("Учиться", "учиться", "и ещё раз учиться!")
}
// Функции могут иметь варьируемое количество параметров.
func learnVariadicParams(myStrings ...interface{}) {
// Вывести все параметры с помощью итерации.
for _, param := range myStrings {
fmt.Println("param:", param)
}
// Передать все варьируемые параметры.
fmt.Println("params:", fmt.Sprintln(myStrings...))
learnErrorHandling()
}
func learnErrorHandling() {
// Идиома ", ok" служит для обозначения корректного срабатывания чего-либо.
m := map[int]string{3: "three", 4: "four"}
if x, ok := m[1]; !ok { // ok будет false, потому что 1 нет в map-е.
fmt.Println("тут никого нет")
} else {
fmt.Print(x) // x содержал бы значение, если бы 1 был в map-е.
}
// Идиома ", err" служит для обозначения была ли ошибка или нет.
if _, err := strconv.Atoi("non-int"); err != nil { // _ игнорирует значение
// выведет "strconv.ParseInt: parsing "non-int": invalid syntax"
fmt.Println(err)
}
// Мы ещё обратимся к интерфейсам чуть позже, а пока...
learnConcurrency()
}
// c – это тип данных channel (канал), объект для конкурентного взаимодействия.
func inc(i int, c chan int) {
c <- i + 1 // когда channel слева, <- является оператором "отправки".
}
// Будем использовать функцию inc для конкурентной инкрементации чисел.
func learnConcurrency() {
// Тот же make, что и в случае со slice. Он предназначен для выделения
// памяти и инициализации типов slice, map и channel.
c := make(chan int)
// Старт трех конкурентных goroutine. Числа будут инкрементированы
// конкурентно и, может быть параллельно, если машина правильно
// сконфигурирована и позволяет это делать. Все они будут отправлены в один
// и тот же канал.
go inc(0, c) // go начинает новую горутину.
go inc(10, c)
go inc(-805, c)
// Считывание всех трех результатов из канала и вывод на экран.
// Нет никакой гарантии в каком порядке они будут выведены.
fmt.Println(<-c, <-c, <-c) // канал справа, <- обозначает "получение".
cs := make(chan string) // другой канал, содержит строки.
cc := make(chan chan string) // канал каналов со строками.
go func() { c <- 84 }() // пуск новой горутины для отправки значения
go func() { cs <- "wordy" }() // ещё раз, теперь для cs
// Select тоже что и switch, но работает с каналами. Он случайно выбирает
// готовый для взаимодействия канал.
select {
case i := <-c: // полученное значение можно присвоить переменной
fmt.Printf("это %T", i)
case <-cs: // либо значение можно игнорировать
fmt.Println("это строка")
case <-cc: // пустой канал, не готов для коммуникации.
fmt.Println("это не выполнится.")
}
// В этой точке значение будет получено из c или cs. Одна горутина будет
// завершена, другая останется заблокированной.
learnWebProgramming() // Да, Go это может.
}
// Всего одна функция из пакета http запускает web-сервер.
func learnWebProgramming() {
// У ListenAndServe первый параметр это TCP адрес, который нужно слушать.
// Второй параметр это интерфейс типа http.Handler.
err := http.ListenAndServe(":8080", pair{})
fmt.Println(err) // не игнорируйте сообщения об ошибках
}
// Реализация интерфейса http.Handler для pair, только один метод ServeHTTP.
func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Обработка запроса и отправка данных методом из http.ResponseWriter
w.Write([]byte("You learned Go in Y minutes!"))
}
func requestServer() {
resp, err := http.Get("http://localhost:8080")
fmt.Println(err)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Printf("\nWebserver said: `%s`", string(body))
}
```
## Что дальше
Основа всех основ в Go это [официальный веб сайт](https://go.dev/).
Там можно пройти туториал, поиграться с интерактивной средой Go и почитать
объёмную документацию.
Для живого ознакомления рекомендуется почитать исходные коды [стандартной
библиотеки Go](https://go.dev/src/). Отлично задокументированная, она
является лучшим источником для чтения и понимания Go, его стиля и идиом. Либо
можно, кликнув на имени функции в [документации](https://go.dev/pkg/),
перейти к ее исходным кодам.
|