Learn in 10 minutes

Learn in 10 minutes

Изучите Go за 10 минут

Go (также известный как Golang) — это статически типизированный компилируемый язык программирования, разработанный в Google. Он известен своей простотой, эффективностью и отличной поддержкой конкурентности. Это руководство поможет быстро изучить программирование на Go.

1. Написание первой программы на Go

Начнем с простой программы. Создайте файл с именем hello.go и введите следующий код:

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

Сохраните файл и выполните следующую команду в терминале:

go run hello.go

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

Hello, World!

Эта простая программа демонстрирует базовую структуру Go:

  • package main объявляет имя пакета
  • import "fmt" импортирует пакет форматирования для операций ввода-вывода
  • func main() — точка входа в программу
  • fmt.Println() выводит текст в консоль

2. Базовый синтаксис

Go имеет чистый и простой синтаксис. В отличие от Python, Go использует фигурные скобки {} для определения блоков кода и требует точки с запятой в конце операторов (хотя они обычно вставляются автоматически).

// Это однострочный комментарий
fmt.Println("Hello, World!")

/*
Это многострочный комментарий,
занимающий несколько строк
*/

Основные правила синтаксиса в Go:

  • Блоки кода: Определяются фигурными скобками {}
  • Комментарии: Однострочные комментарии начинаются с //, многострочные с /* */
  • Операторы: Заканчиваются точками с запятой (вставляются автоматически)
  • Объявление пакета: Каждый файл начинается с объявления пакета

3. Переменные и типы данных

Go статически типизирован, что означает необходимость объявления типов переменных. Однако Go поддерживает вывод типов с помощью оператора :=.

Способы объявления переменных:

// Явное объявление типа
var name string = "John"
var age int = 25

// Вывод типа
name := "John"
age := 25

// Объявление нескольких переменных
var x, y int = 10, 20
x, y := 10, 20

Основные базовые типы данных в Go:

  • Целочисленные типы: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr
  • Вещественные типы: float32, float64
  • Строки: string
  • Логический тип: bool
  • Комплексные типы: complex64, complex128

3.1 Числовые типы

Go предоставляет различные числовые типы для разных случаев использования:

// Целочисленные типы
var age int = 25
var smallNumber int8 = 127
var largeNumber int64 = 9223372036854775807

// Вещественные типы
var temperature float32 = 36.5
var pi float64 = 3.14159265359

// Комплексные числа
var complexNum complex64 = 3 + 4i

3.2 Строковый тип

Строки в Go — это последовательности байтов и они неизменяемы:

// Объявление строки
var greeting string = "Hello, Go!"
name := "Alice"

// Операции со строками
fmt.Println(len(greeting))        // Длина строки
fmt.Println(greeting[0])          // Доступ к первому символу (байту)
fmt.Println(greeting[0:5])        // Срез строки
fmt.Println(strings.ToUpper(name)) // Преобразование в верхний регистр

3.3 Логический тип

Логический тип имеет два значения: true и false:

var isActive bool = true
var isComplete bool = false

// Логические операции
result1 := true && false  // false
result2 := true || false  // true
result3 := !true          // false

4. Константы

Константы объявляются с помощью ключевого слова const и не могут быть изменены:

const Pi = 3.14159
const MaxUsers = 1000

// Несколько констант
const (
    StatusOK = 200
    StatusNotFound = 404
    StatusError = 500
)

// Типизированные константы
const Version string = "1.0.0"

5. Структуры данных

Go предоставляет несколько встроенных структур данных для хранения и обработки информации.

5.1 Массивы

Массивы — это последовательности фиксированного размера элементов одного типа:

// Объявление массива
var numbers [5]int = [5]int{1, 2, 3, 4, 5}
names := [3]string{"Alice", "Bob", "Charlie"}

// Доступ к элементам
fmt.Println(numbers[0])  // 1
numbers[0] = 10         // Изменение элемента

// Длина массива
fmt.Println(len(numbers)) // 5

5.2 Срезы

Срезы — это динамические массивы, которые могут расти и уменьшаться:

// Объявление среза
numbers := []int{1, 2, 3, 4, 5}
var emptySlice []int

// Создание срезов из массивов
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4]  // [2, 3, 4]

// Операции со срезами
numbers = append(numbers, 6)      // Добавление элемента
numbers = append(numbers, 7, 8, 9) // Добавление нескольких элементов

// Емкость и длина среза
fmt.Println(len(numbers)) // Длина: 9
fmt.Println(cap(numbers)) // Емкость: 10 (может варьироваться)

5.3 Карты

Карты — это неупорядоченные коллекции пар ключ-значение:

// Объявление карты
student := map[string]interface{}{
    "name": "John",
    "age":  20,
    "major": "Computer Science",
}

// Альтернативное объявление
var scores map[string]int = make(map[string]int)
scores["math"] = 95
scores["science"] = 88

// Доступ и изменение
fmt.Println(student["name"])
student["age"] = 21
student["gpa"] = 3.8

// Безопасный доступ
if phone, exists := student["phone"]; exists {
    fmt.Println(phone)
} else {
    fmt.Println("Телефон не указан")
}

// Итерация по карте
for key, value := range student {
    fmt.Printf("%s: %v\n", key, value)
}

5.4 Структуры

Структуры — это коллекции полей, которые могут иметь разные типы:

// Определение структуры
type Person struct {
    Name string
    Age  int
    City string
}

// Создание экземпляров структуры
person1 := Person{"Alice", 25, "New York"}
person2 := Person{
    Name: "Bob",
    Age:  30,
    City: "London",
}

// Доступ к полям
fmt.Println(person1.Name)
person1.Age = 26

6. Операции и операторы

Go предоставляет богатый набор операторов для различных вычислений и сравнений.

  • Арифметические операторы: +, -, *, /, % (остаток от деления)
  • Операторы сравнения: ==, !=, >, <, >=, <=
  • Логические операторы: &&, ||, !
  • Побитовые операторы: &, |, ^, <<, >>
  • Операторы присваивания: =, +=, -=, *=, /=

6.1 Арифметические операторы

a, b := 10, 3

fmt.Printf("Сложение: %d\n", a + b)      // 13
fmt.Printf("Вычитание: %d\n", a - b)   // 7
fmt.Printf("Умножение: %d\n", a * b)  // 30
fmt.Printf("Деление: %d\n", a / b)      // 3
fmt.Printf("Остаток от деления: %d\n", a % b)      // 1

6.2 Операторы сравнения

x, y := 5, 10

fmt.Printf("Равно: %t\n", x == y)     // false
fmt.Printf("Не равно: %t\n", x != y) // true
fmt.Printf("Больше: %t\n", x > y)  // false
fmt.Printf("Меньше: %t\n", x < y)  // true

6.3 Логические операторы

a, b := true, false

fmt.Printf("Логическое И: %t\n", a && b)  // false
fmt.Printf("Логическое ИЛИ: %t\n", a || b)    // true
fmt.Printf("Логическое НЕ: %t\n", !a)    // false

7. Управляющие конструкции

Go предоставляет несколько управляющих конструкций для управления выполнением программы.

7.1 Условные операторы if

age := 20
if age >= 18 {
    fmt.Println("Взрослый")
} else if age >= 13 {
    fmt.Println("Подросток")
} else {
    fmt.Println("Ребенок")
}

// if с коротким оператором
if score := 85; score >= 90 {
    fmt.Println("Оценка: A")
} else if score >= 80 {
    fmt.Println("Оценка: B")
} else {
    fmt.Println("Оценка: C")
}

7.2 Циклы for

Go имеет только одну конструкцию цикла: for

// Базовый цикл for
for i := 0; i < 5; i++ {
    fmt.Println(i)
}

// Цикл в стиле while
count := 0
for count < 5 {
    fmt.Println(count)
    count++
}

// Бесконечный цикл
for {
    fmt.Println("Этот цикл будет выполняться вечно")
    break // Используйте break для выхода
}

// Цикл range (для срезов, массивов, карт)
fruits := []string{"apple", "banana", "cherry"}
for index, fruit := range fruits {
    fmt.Printf("%d: %s\n", index, fruit)
}

7.3 Операторы switch

day := "Monday"
switch day {
case "Monday":
    fmt.Println("Начало недели")
case "Friday":
    fmt.Println("Выходные близко")
case "Saturday", "Sunday":
    fmt.Println("Выходные!")
default:
    fmt.Println("Обычный день")
}

// Switch без выражения
score := 85
switch {
case score >= 90:
    fmt.Println("Оценка: A")
case score >= 80:
    fmt.Println("Оценка: B")
case score >= 70:
    fmt.Println("Оценка: C")
default:
    fmt.Println("Оценка: F")
}

8. Функции

Функции в Go являются объектами первого класса и поддерживают множественные возвращаемые значения.

8.1 Базовые функции

func greet(name string) string {
    return "Hello, " + name + "!"
}

// Вызов функции
message := greet("John")
fmt.Println(message)

8.2 Множественные возвращаемые значения

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("нельзя делить на ноль")
    }
    return a / b, nil
}

// Использование множественных возвращаемых значений
result, err := divide(10, 2)
if err != nil {
    fmt.Println("Ошибка:", err)
} else {
    fmt.Println("Результат:", result)
}

8.3 Именованные возвращаемые значения

func calculateRectangle(width, height float64) (area float64, perimeter float64) {
    area = width * height
    perimeter = 2 * (width + height)
    return // "голый" return
}

area, perimeter := calculateRectangle(5, 3)
fmt.Printf("Площадь: %.2f, Периметр: %.2f\n", area, perimeter)

8.4 Функции с переменным числом аргументов

func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

fmt.Println(sum(1, 2, 3, 4))  // 10
fmt.Println(sum(5, 10, 15))   // 30

9. Указатели

Go имеет указатели, но с более простым синтаксисом, чем C/C++:

func modifyValue(x *int) {
    *x = *x * 2
}

func main() {
    value := 10
    fmt.Println("До:", value) // 10

    modifyValue(&value)
    fmt.Println("После:", value)  // 20
}

10. Методы

Методы — это функции с получателем:

type Rectangle struct {
    Width  float64
    Height float64
}

// Метод с получателем по значению
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// Метод с получателем по указателю
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

rect := Rectangle{Width: 5, Height: 3}
fmt.Println("Площадь:", rect.Area()) // 15

rect.Scale(2)
fmt.Println("Масштабированная площадь:", rect.Area()) // 60

11. Интерфейсы

Интерфейсы определяют сигнатуры методов, которые могут реализовывать типы:

type Shape interface {
    Area() float64
    Perimeter() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14159 * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * 3.14159 * c.Radius
}

func printShapeInfo(s Shape) {
    fmt.Printf("Площадь: %.2f, Периметр: %.2f\n", s.Area(), s.Perimeter())
}

circle := Circle{Radius: 5}
printShapeInfo(circle)

12. Обработка ошибок

Go использует явную обработку ошибок вместо исключений:

func readFile(filename string) (string, error) {
    data, err := os.ReadFile(filename)
    if err != nil {
        return "", fmt.Errorf("не удалось прочитать файл %s: %w", filename, err)
    }
    return string(data), nil
}

content, err := readFile("example.txt")
if err != nil {
    fmt.Println("Ошибка:", err)
    return
}
fmt.Println("Содержимое:", content)

13. Конкурентность с горутинами

Горутины — это легковесные потоки, управляемые средой выполнения Go:

func worker(id int) {
    for i := 0; i < 3; i++ {
        fmt.Printf("Работник %d: %d\n", id, i)
        time.Sleep(time.Millisecond * 100)
    }
}

func main() {
    // Запуск нескольких горутин
    for i := 1; i <= 3; i++ {
        go worker(i)
    }

    // Ожидание завершения горутин
    time.Sleep(time.Second)
    fmt.Println("Все работники завершили работу")
}

14. Каналы

Каналы используются для общения между горутинами:

func producer(ch chan<- int) {
    for i := 0; i < 5; i++ {
        ch <- i // Отправка значения в канал
        time.Sleep(time.Millisecond * 100)
    }
    close(ch) // Закрытие канала по завершении
}

func consumer(ch <-chan int) {
    for value := range ch {
        fmt.Println("Получено:", value)
    }
}

func main() {
    ch := make(chan int, 3) // Буферизованный канал

    go producer(ch)
    consumer(ch)

    fmt.Println("Общение через канал завершено")
}

15. Операции с файлами

Go предоставляет простые методы для чтения и записи файлов:

// Чтение файлов
data, err := os.ReadFile("example.txt")
if err != nil {
    fmt.Println("Ошибка чтения файла:", err)
    return
}
fmt.Println("Содержимое файла:", string(data))

// Запись файлов
content := "Hello, Go!\n"
err = os.WriteFile("output.txt", []byte(content), 0644)
if err != nil {
    fmt.Println("Ошибка записи файла:", err)
    return
}
fmt.Println("Файл успешно записан")

16. Пакеты и модули

Модули Go управляют зависимостями и версиями пакетов:

// Импорт стандартных библиотечных пакетов
import (
    "fmt"
    "math"
    "strings"
)

func main() {
    fmt.Println(math.Sqrt(16))        // 4
    fmt.Println(strings.ToUpper("go")) // GO
}

Чтобы создать собственный пакет, создайте каталог с именем вашего пакета и экспортируйте функции, используя заглавные буквы в их именах.

17. Тестирование

Go имеет встроенную поддержку тестирования:

// В файле math_test.go
package main

import "testing"

func TestAdd(t *testing.T) {
    result := add(2, 3)
    expected := 5
    if result != expected {
        t.Errorf("add(2, 3) = %d; ожидалось %d", result, expected)
    }
}

func add(a, b int) int {
    return a + b
}

Запустите тесты командой: go test

18. Лучшие практики

  • Используйте gofmt для форматирования кода
  • Следуйте соглашениям об именовании в Go (camelCase для переменных, PascalCase для экспортируемых элементов)
  • Обрабатывайте ошибки явно
  • Используйте интерфейсы для абстракции
  • Предпочитайте композицию наследованию
  • Пишите комплексные тесты
  • Используйте стандартную библиотеку везде, где это возможно

Это руководство охватывает основные возможности программирования на Go. С практикой вы сможете создавать эффективные и конкурентные приложения, используя мощные возможности Go.