--- 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"] --- 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/), перейти к ее исходным кодам.