Лекция 2. Переменные, константы и базовые типы Go
Цели лекции
После лекции студент должен:
- объявлять переменные через
varи:=; - выбирать между переменной и константой;
- понимать zero value;
- различать целые, вещественные и логические типы;
- понимать различие между
string,byteиrune; - выполнять явные преобразования типов;
- создавать именованные типы;
- использовать
iotaдля связанных констант; - объяснять, чем типизация Go отличается от Python.
1. Переменная и её тип
Переменная — именованная область памяти, содержащая значение определённого типа. В Go тип переменной не меняется после объявления.
package main
import "fmt"
func main() { var age int = 20 fmt.Println(age)
// age = "двадцать" // ошибка: string нельзя использовать как int}Python разрешает тому же имени ссылаться на другое значение:
age = 20age = "двадцать"В Go такое изменение типа потребовало бы отдельной переменной.
2. Объявление через var
Полная форма:
var studentName string = "Анна"var course int = 2Если начальное значение присутствует, тип часто можно не писать:
var city = "Казань" // stringvar rating = 4.75 // float64Можно объявить несколько переменных:
var width, height int = 10, 20Или использовать группировку на уровне пакета:
var ( applicationName = "Task Manager" debugMode = false maxConnections = 100)Группировка удобна для связанных объявлений, но не должна превращаться в глобальное хранилище состояния программы.
3. Короткое объявление :=
Внутри функции чаще используется короткая форма:
name := "Иван"age := 19average := 4.6Компилятор выводит тип из правой части.
:= можно использовать только внутри функций:
package main
// version := "1.0" // ошибка на уровне пакетаvar version = "1.0"
func main() { message := "Программа запущена" println(message)}Повторное присваивание выполняется через =:
score := 10score = 15Запись score := 15 в том же блоке без новой переменной вызовет ошибку.
Смешанное короткое объявление
Если хотя бы одна переменная слева новая, := допустим:
name := "Алиса"name, age := "Борис", 21Здесь name получает новое значение, а age объявляется впервые. Такой синтаксис
часто встречается при получении значения и ошибки:
data, err := loadData()4. Zero value
В Go каждая объявленная переменная сразу имеет корректное значение. Если инициализатор не указан, используется нулевое значение типа.
var count intvar price float64var enabled boolvar title string
fmt.Println(count) // 0fmt.Println(price) // 0fmt.Println(enabled) // falsefmt.Println(title) // пустая строкаОсновные zero values:
| Тип | Zero value |
|---|---|
| целые числа | 0 |
| вещественные числа | 0.0 |
bool | false |
string | "" |
| pointer, slice, map, channel, function, interface | nil |
Zero value не всегда означает корректное предметное значение. Например, нулевая цена может быть допустима, а нулевой возраст пользователя — нет. Такие инварианты проверяются логикой программы.
5. Константы
Константа объявляется через const:
const Pi = 3.1415926535const CourseName = "Язык программирования Go"const MaxAttempts = 3Константу нельзя изменить:
const TaxRate = 0.2// TaxRate = 0.25 // ошибка компиляцииКонстантами могут быть строки, числа, логические значения и выражения, которые компилятор способен вычислить заранее:
const SecondsPerMinute = 60const MinutesPerHour = 60const SecondsPerHour = SecondsPerMinute * MinutesPerHourРезультат функции во время выполнения константой быть не может:
// const StartedAt = time.Now() // ошибкаvar startedAt = time.Now()Go не использует ключевые слова let и final. Выбор между изменяемым значением
и константой выражается через var и const.
6. Целые типы
Знаковые типы:
int8,int16,int32,int64;int— размер зависит от целевой архитектуры, обычно 32 или 64 бита.
Беззнаковые типы:
uint8,uint16,uint32,uint64;uint;uintptrдля низкоуровневых задач.
Для большинства счётчиков, индексов и размеров внутри программы используют
int:
students := 25completed := 18remaining := students - completedФиксированные типы важны при работе с бинарными форматами, протоколами и внешними API.
Переполнение
Диапазон int8 — от -128 до 127:
var value int8 = 127// value = value + 1 // результат не помещается в int8Компилятор обнаруживает переполнение константы, но арифметика переменных фиксированной ширины может переполняться во время выполнения. Выбор типа остаётся ответственностью программиста.
7. Вещественные числа
Go предоставляет:
float32;float64.
По умолчанию литерал с дробной частью выводится как float64:
temperature := 21.5var precise float64 = 3.1415926535var compact float32 = 1.25Вещественные числа представлены приближённо:
result := 0.1 + 0.2fmt.Printf("%.20f\n", result)Поэтому денежные суммы часто хранят в минимальных единицах:
priceKopecks := 19990 // 199 рублей 90 копеекДля серьёзных финансовых расчётов используют целочисленное представление или специализированный decimal-тип.
8. Логический тип
bool принимает только true или false:
isActive := truehasAccess := falseЧисла и строки нельзя использовать как логические значения:
count := 3// if count { } // ошибка
if count > 0 { fmt.Println("Есть элементы")}В отличие от Python, пустая строка, ноль и пустая коллекция не преобразуются в
false автоматически.
9. Строки
Строковый тип называется string:
name := "Мария"message := "Привет, " + name + "!"Интерполяции вида Python f-string в Go нет. Для форматирования используется
пакет fmt:
age := 20text := fmt.Sprintf("Студенту %s %d лет", name, age)fmt.Println(text)Часто используемые спецификаторы:
| Спецификатор | Назначение |
|---|---|
%s | строка |
%d | целое число |
%f | вещественное число |
%.2f | два знака после точки |
%t | логическое значение |
%v | значение в стандартном виде |
%T | тип значения |
Raw string записывается в обратных кавычках и может занимать несколько строк:
query := `SELECT id, nameFROM studentsWHERE active = true`Escape-последовательности внутри raw string не обрабатываются.
10. byte, rune и Unicode
byte — псевдоним uint8, а rune — псевдоним int32.
var b byte = 'A'var r rune = 'Я'Одинарные кавычки обозначают rune, а не строку:
letter := 'Ж' // runeword := "Ж" // stringСтроки Go хранят байты, обычно в UTF-8. Поэтому len возвращает число байтов:
fmt.Println(len("Go")) // 2fmt.Println(len("Привет")) // 12, а не 6Количество Unicode-символов можно получить после преобразования в []rune:
fmt.Println(len([]rune("Привет"))) // 6Подробная работа со строками рассматривается в лекции 6.
11. Вывод типов
Компилятор определяет тип по начальному значению:
count := 42 // intratio := 0.75 // float64language := "Go" // stringavailable := true // boolsymbol := 'G' // runeТип можно проверить во время эксперимента:
fmt.Printf("%T\n", count)fmt.Printf("%T\n", symbol)В рабочем коде обычно не печатают типы — они известны компилятору и редактору.
12. Явные преобразования
Go не выполняет большинство числовых преобразований автоматически:
var count int = 10var ratio float64 = 2.5
// result := count * ratio // ошибка: разные типыresult := float64(count) * ratiofmt.Println(result)Преобразование может потерять данные:
price := 19.95whole := int(price)fmt.Println(whole) // 19Преобразование числа в строку не выполняется через string(number) в обычном
смысле. Для десятичной записи используют strconv:
age := 20text := strconv.Itoa(age)
value, err := strconv.Atoi("42")strconv.Atoi возвращает число и ошибку. Ошибки подробно изучаются позже, но
уже сейчас нельзя игнорировать возможность некорректного ввода:
value, err := strconv.Atoi("42")if err != nil { fmt.Println("Некорректное число:", err) return}fmt.Println(value)13. Несколько присваиваний
Go позволяет присвоить несколько значений одновременно:
x, y := 10, 20x, y = y, xfmt.Println(x, y) // 20 10Функции также могут возвращать несколько значений. Это используется для пары «результат и ошибка» или «значение и признак наличия».
14. Пустой идентификатор _
Иногда одно из полученных значений не нужно:
value, _ := strconv.Atoi("42")fmt.Println(value)_ называется blank identifier. Значение присваивается ему и сразу отбрасывается.
Игнорировать ошибки таким способом без причины нельзя: это скрывает проблемы.
Допустимый пример — сознательно ненужный индекс:
for _, name := range []string{"Анна", "Борис"} { fmt.Println(name)}15. Именованные типы
Можно создать новый тип на основе существующего:
type UserID int64type Celsius float64
var id UserID = 1001var temperature Celsius = 21.5UserID и int64 становятся разными типами:
var raw int64 = 1001// id = raw // ошибкаid = UserID(raw)Это помогает не смешивать значения с одинаковым физическим представлением, но разным смыслом: идентификатор пользователя, идентификатор заказа и обычный счётчик.
Псевдоним типа объявляется через =:
type Identifier = int64Псевдоним не создаёт новый тип. В прикладном коде чаще нужны именованные типы, которые позволяют компилятору различать предметные значения.
16. iota
iota создаёт последовательность связанных целочисленных констант:
type Status int
const ( StatusNew Status = iota StatusInProgress StatusDone)Значения будут равны 0, 1 и 2. Начало с нуля полезно: zero value типа Status
соответствует StatusNew.
Если ноль должен означать неизвестное состояние:
const ( StatusUnknown Status = iota StatusNew StatusInProgress StatusDone)iota не создаёт полноценное перечисление с закрытым множеством вариантов.
Переменная типа Status всё ещё может получить другое целое значение через
явное преобразование. Корректность проверяет программа.
17. Область видимости и затенение
Переменная видна внутри блока, где объявлена:
func main() { message := "внешнее значение"
if true { message := "внутреннее значение" fmt.Println(message) }
fmt.Println(message)}Внутренняя переменная затеняет внешнюю. Это допустимо, но может вызвать ошибку в
логике, особенно при использовании :=:
result, err := load()if err != nil { return}
if needReload { result, err := loadAgain() // новые переменные внутри if _ = result _ = err}Нужно внимательно различать := и =.
18. Типичные ошибки
Объявленная, но неиспользуемая переменная
name := "Анна" // ошибка, если name дальше не используетсяКомпилятор не разрешает оставлять неиспользуемые локальные переменные.
Смешивание числовых типов
var a int = 10var b int64 = 20// fmt.Println(a + b) // ошибкаfmt.Println(int64(a) + b)Ожидание автоматического преобразования в bool
text := "Go"// if text { } // ошибкаif text != "" { fmt.Println(text)}Потеря дробной части
average := int(4.9) // 4Необоснованное использование глобальных переменных
Глобальное изменяемое состояние усложняет тестирование и конкурентный доступ. Переменные следует объявлять в минимально необходимой области.
19. Практический пример
package main
import "fmt"
type StudentID int64
const ( TotalCourses = 4 ExcellentGPA = 4.5)
func main() { var id StudentID = 1001 name := "Анна Петрова" birthYear := 2006 currentYear := 2026 course := 2 average := 4.75 active := true
age := currentYear - birthYear remainingCourses := TotalCourses - course
fmt.Printf("ID: %d\n", id) fmt.Printf("Имя: %s\n", name) fmt.Printf("Возраст: %d\n", age) fmt.Printf("Курс: %d\n", course) fmt.Printf("Осталось курсов: %d\n", remainingCourses) fmt.Printf("Средний балл: %.2f\n", average) fmt.Printf("Активен: %t\n", active) fmt.Printf("Отличник: %t\n", average >= ExcellentGPA)}Пример показывает вывод типов, именованный тип, типизированные и нетипизированные константы, форматированный вывод и явные предметные имена.
20. Вопросы для самопроверки
- Чем
varотличается от:=? - Где разрешено короткое объявление?
- Что такое zero value?
- Какие значения являются zero value для
int,boolиstring? - Почему
intнельзя автоматически сложить сint64? - Чем
byteотличается отrune? - Почему
len("Привет")не равен количеству русских букв? - Как преобразовать строку
"42"в число? - Чем именованный тип отличается от псевдонима?
- Для чего используется
iota? - Что делает идентификатор
_? - Почему затенение переменных может быть опасно?
Итоги
Go сочетает строгую статическую типизацию с компактным синтаксисом. Переменные
можно объявлять явно через var или кратко через :=, а неизменяемые значения —
через const. Каждый тип имеет zero value, числовые преобразования выполняются
явно, а именованные типы помогают выражать смысл предметной области.