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