learnxinyminutes-docs/ru/go.md
2024-12-08 23:20:53 -07:00

344 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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