Golang: Go, Go, Go
Go на нашем проекте подаётся как «второй язык» — это значит, что мы предполагаем наличие опыта программирования у тех, кто берётся его изучать. В первую очередь — это знание базовых концепций: переменные, типы данных, условные конструкции, циклы, функции, структуры, и, возможно, работа с указателями или замыканиями.
Изучение второго языка значительно проще первого, поэтому и подход к подаче материала другой. Мы обзорно касаемся синтаксиса и особенностей языка, чтобы быстрее перейти к практике, ради которой Go и выбирается — это простая и быстрая разработка, в первую очередь серверных приложений.
В чём соль?
Go задуман как современное переосмысление языка C: он такой же компактный, низкоуровневый и предсказуемый, но с безопасной моделью памяти, встроенной системой модулей и примитивами для конкурентного программирования. От C он унаследовал:
- синтаксис на базе блоков и функций
- отсутствие классов и наследования
- строгую типизацию и компиляцию в бинарники
Но при этом Go радикально проще в использовании, чем Си
- нет ручного управления памятью — работает сборщик мусора
- нет макросов, препроцессоров и сложной линковки
- встроенная система модулей и форматирования
// Статическая типизация — типы известны на этапе компиляции
var n int = 42
// Компактное определение переменной с неявным типом
msg := "Hello, Go"
// Объявление и вызов функции
func add(a, b int) int {
return a + b
}
Go — язык с намеренно минимальным синтаксисом. Здесь мало ключевых слов, нет перегрузки функций, нет классов, нет try/catch, нет тернарного оператора и других привычных конструкций из C-подобных языков.
Но при этом всё, что нужно, есть:
- структуры и методы вместо классов
- интерфейсы с неявной реализацией
- функции как значения и замыкания
- корутины (горутины) и каналы — мощная модель конкурентности
Ошибки — часть логики
В Go нет исключений. Вместо них — явная обработка ошибок с помощью возвращаемых значений:
data, err := os.ReadFile("file.txt")
if err != nil {
return fmt.Errorf("ошибка чтения: %w", err)
}
Это дисциплинирует: любая потенциальная ошибка должна быть осмыслена и обработана. Модель ошибок встроена в повседневный синтаксис языка — и делает его надёжным по умолчанию.
Интерфейсы и структурная типизация
Go использует структурную типизацию, а не номинативную, как во многих других языках. Это значит, что чтобы тип реализовал интерфейс, не нужно явно указывать об этом — достаточно, чтобы у него были нужные методы. Интерфейсы в Go — это просто набор требований к поведению, и любое значение, которое этим требованиям соответствует, автоматически удовлетворяет интерфейсу.
type Reader interface {
Read(p []byte) (n int, err error)
}
type File struct{}
func (f File) Read(p []byte) (int, error) {
// реализация
return 0, nil
}
func use(r Reader) {
// можно передать File, если он реализует Read
}
Конкурентность в Go — ключевая особенность
Go был спроектирован для простого и безопасного параллельного программирования. Вместо потоков ОС и явных примитивов синхронизации здесь используются горутины. При этом Go по умолчанию эффективно утилизирует все доступные процессорные ядра, обеспечивая настоящую параллельность выполнения.
go fmt.Println("выполняется параллельно")
Горутины — это лёгкие потоки исполнения, встроенные в язык Go. Они запускаются с помощью ключевого слова go и позволяют выполнять функции конкурентно, не занимаясь управлением потоками вручную. В отличие от потоков операционной системы, горутины работают в рамках рантайма Go и масштабируются тысячами: они дешевы по памяти и быстро переключаются без участия ОС. Это делает их удобным инструментом для написания высокопроизводительных, неблокирующих приложений.
Заключение
Go может показаться простым — и это не обман. Его минимализм — осознанный выбор. Если вы устали от перегруженности, магии и бесконечных зависимостей — Go будет глотком свежего воздуха. Да, тут мало «сахара» и удобств, но за это вы получаете скорость, надёжность и стабильность. Именно поэтому Go выбирают для создания системных утилит, серверов, инфраструктуры и инструментов — таких как Docker, Kubernetes, Prometheus и другие.
Если вы умеете программировать, то Go станет для вас языком, который просто делает свою работу — и делает её хорошо.
Задание
Написать интересный код самостоятельно на текущем уровне будет затруднительно, поэтому скопируйте код ниже.
wg := sync.WaitGroup{}
for i := range 3 {
wg.Add(1)
go func() {
fmt.Println("Go! " + strconv.Itoa(i))
wg.Done()
}()
}
wg.Wait()
Полезное
Команда проекта находится в телеграм-сообществе. Там можно задать любой вопрос и повлиять на проект
Если вы зашли в тупик, то самое время поговорить с нашим асситентом Тота во вкладке "ИИ-помощник":
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи. В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в обратной связи нашего сообщества
Ваше упражнение проверяется по этим тестам
package main
import (
"fmt"
"log"
"os/exec"
"testing"
)
func TestGoGoGo(t *testing.T) {
cmd := exec.Command("go", "run", "solution.go")
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("exec command: %s\n", err)
}
fmt.Println(string(out))
// Output:
// Go!
// Go!
// Go!
}
Решение учителя откроется через:
20:00
