Введение в Go: синтаксис и основные конструкции
Введение в Go: синтаксис и основные конструкции
Go (или Golang) — это компилируемый, статически типизированный язык программирования, разработанный в Google. Для разработчика на Python переход на Go может показаться шагом в сторону большей строгости: здесь нет динамической типизации, декораторов или исключений в привычном понимании. Однако взамен Go предлагает производительность, близкую к C, простоту синтаксиса и мощные инструменты для работы с конкурентностью.
В этой статье мы разберем фундаментальные строительные блоки языка, необходимые для написания вашей первой программы.
Структура программы и пакеты
В Python код организуется в модули и пакеты. В Go концепция похожа, но более формализована. Любой файл с исходным кодом на Go обязан начинаться с объявления пакета.
Пакет main
Точкой входа в приложение является пакет main. Если вы хотите создать исполняемый файл (а не библиотеку), ваш код должен содержать функцию main внутри пакета main.
package main
import "fmt"
func main() {
fmt.Println("Hello, Go for Python Developers!")
}
Разберем этот пример:
package main: Объявляет, что данный файл принадлежит пакетуmain.import "fmt": Импортирует пакетfmt(format) из стандартной библиотеки для ввода/вывода (аналогprintв Python, но мощнее).func main(): Объявляет функцию. Обратите внимание: фигурные скобки{}обязательны, и открывающая скобка должна находиться на той же строке, что и объявление функции. Это требование компилятора, которое обеспечивает единый стиль кода (gofmt).
Переменные и типизация
Go — язык со строгой статической типизацией. Это означает, что тип переменной определяется на этапе компиляции и не может быть изменен в процессе выполнения.
Объявление переменных
Существует два основных способа объявления переменных:
- Полное объявление с использованием ключевого слова
var. Используется, когда нужно объявить переменную без инициализации или явно указать тип. - Краткое объявление с использованием оператора
:=. Доступно только внутри функций.
package main
import "fmt"
func main() {
// Полное объявление
var x int = 10
// Тип выводится автоматически
var y = 20
// Краткое объявление (наиболее часто используется)
z := 30
// Множественное присваивание
name, age := "Alice", 25
fmt.Println(x, y, z, name, age)
}
Нулевые значения (Zero Values)
В отличие от Python, где неинициализированная переменная может вызвать ошибку NameError или быть равна None, в Go переменные всегда имеют значение по умолчанию, называемое нулевым значением:
0для числовых типов.falseдляbool.""(пустая строка) дляstring.nilдля указателей, интерфейсов, слайсов, каналов и карт.
Это гарантирует, что в памяти не будет "мусора".
Базовые типы данных
Go предоставляет стандартный набор примитивных типов:
- Целые числа:
int,int8,int16,int32,int64(и беззнаковыеuint). Типintплатформозависимый (32 или 64 бита). - Числа с плавающей точкой:
float32,float64. Стандартом де-факто являетсяfloat64(аналогfloatв Python). - Булев тип:
bool(значенияtrueиfalse). - Строки:
string. В Go строки — это неизменяемые последовательности байтов (UTF-8).
Управляющие конструкции
Синтаксис управляющих конструкций в Go минималистичен. Здесь нет круглых скобок вокруг условий, но фигурные скобки для тела блока обязательны.
Условный оператор if
Особенность if в Go — возможность выполнить короткую инструкцию перед проверкой условия. Это часто используется для получения значения и немедленной его проверки.
if x := computeValue(); x > 10 {
fmt.Println("X is big:", x)
} else {
fmt.Println("X is small:", x)
}
// Переменная x доступна только внутри блока if-else
Цикл for
В Go есть только один цикл — for. Он покрывает все сценарии использования while и for из других языков.
- Классический цикл (как в C):
for i := 0; i < 5; i++ {
fmt.Println(i)
}
- Аналог while:
sum := 1
for sum < 1000 {
sum += sum
}
- Бесконечный цикл:
for {
// Делаем работу
if condition {
break
}
}
Указатели
Для Python-разработчика указатели могут показаться архаизмом, но в Go они важны для управления памятью и передачи данных без копирования. Указатель хранит адрес памяти значения, а не само значение.
&(амперсанд) — оператор взятия адреса.*(звездочка) — оператор разыменования (получение значения по адресу).
func main() {
val := 42
p := &val // p теперь хранит адрес val
fmt.Println(*p) // выведет 42
*p = 21 // изменяем значение по адресу, хранящемуся в p
fmt.Println(val) // выведет 21
}
В Go нет арифметики указателей (нельзя сделать p++, чтобы перейти к следующей ячейке памяти), что исключает целый класс ошибок, свойственных C++.
Массивы и Слайсы (Slices)
Массивы
Массив в Go имеет фиксированную длину, которая является частью его типа. [5]int и [10]int — это разные типы.
var a [5]int
a[0] = 10
Из-за фиксированного размера массивы используются редко. Вместо них используются слайсы.
Слайсы (Slices)
Слайс — это динамическая абстракция над массивом. Он похож на list в Python, но работает иначе "под капотом". Слайс состоит из трех компонентов: указателя на базовый массив, длины (len) и вместимости (cap).
// Создание слайса с помощью make
// make([]type, len, cap)
nums := make([]int, 3, 5)
// Добавление элементов
nums = append(nums, 1)
nums = append(nums, 2)
// Срезы (slicing) работают похоже на Python
subSlice := nums[1:3]
Важно: При передаче слайса в функцию копируется только его заголовок (указатель, длина, вместимость), но не данные. Изменение элементов слайса внутри функции отразится на оригинале.
Карты (Maps)
map в Go — это аналог dict в Python. Это неупорядоченная коллекция пар "ключ-значение".
// Инициализация
users := make(map[string]int)
users["Alice"] = 25
users["Bob"] = 30
// Литерал карты
techs := map[string]string{
"backend": "Go",
"frontend": "React",
}
// Проверка наличия ключа
age, ok := users["Charlie"]
if !ok {
fmt.Println("Charlie not found")
}
Обратите внимание на идиому val, ok. При попытке получить значение по несуществующему ключу Go не выбросит исключение, а вернет нулевое значение для типа значения (например, 0 для int). Переменная ok (тип bool) покажет, существовал ли ключ на самом деле.
Функции
Функции в Go являются гражданами первого класса. Ключевая особенность, отличающая Go от многих C-подобных языков — возможность возвращать несколько значений.
func div(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
Экспортируемые имена
В Go нет ключевых слов public, private или protected. Область видимости определяется регистром первой буквы имени:
- Заглавная буква (например,
Println,MyStruct): Имя экспортируемо (public), доступно из других пакетов. - Строчная буква (например,
main,calculate): Имя неэкспортируемо (private), доступно только внутри текущего пакета.
Итоги
Мы рассмотрели базовый синтаксис Go, который послужит фундаментом для дальнейшего изучения. Основные выводы:
- Строгость и простота: Go требует явного объявления типов (или их вывода) и не прощает неиспользуемых переменных или импортов, что поддерживает чистоту кода.
- Нулевые значения: Переменные всегда инициализируются безопасными значениями по умолчанию (
0,false,"",nil). - Указатели: Используются для эффективной передачи данных и изменения состояния, но лишены сложной арифметики.
- Слайсы вместо массивов: Основная структура данных для последовательностей — это слайс, который является "окном" в базовый массив.
- Видимость через регистр:
Public— с большой буквы,private— с маленькой.