Статическая типизация и краткое объявление переменных
Краткое описание
В этом уроке изучаем строгую статическую типизацию Go: почему типы нельзя смешивать без явного приведения, как работает автоматический вывод типов компилятором и когда использовать краткую форму объявления := вместо полной var. Разбираем отличия Go от динамически типизированных языков вроде JavaScript.
Ключевые концепции
Строгая статическая типизация
Статическая типизация означает, что тип переменной определяется на этапе компиляции и не может быть изменён во время выполнения программы.
Правило: если переменная объявлена с типом int, она навсегда остаётся int. Нельзя присвоить ей строку, float или другой тип.
var number int = 42
fmt.Printf("number: %d, тип: %T\n", number, number)
// Можно изменить значение
number = 100 // OK
// НЕЛЬЗЯ изменить тип
// number = "строка" // ОШИБКА компиляции
// number = 3.14 // ОШИБКА компиляции
Отличие от JavaScript
В JavaScript (динамическая типизация) переменная может менять тип:
let val = 42;
console.log(typeof val); // "number"
val = "123";
console.log(typeof val); // "string" - тип изменился!
В Go это невозможно — тип фиксируется навсегда при объявлении.
Запрет операций между разными типами
Go не имеет автоматического приведения типов при операциях. Нельзя смешивать int и float64:
var intNum int = 10
var floatNum float64 = 3.14
// ОШИБКА компиляции!
// var result = intNum + floatNum
// invalid operation: intNum + floatNum (mismatched types int and float64)
Компилятор откажется компилировать такой код.
Явное приведение типов
Синтаксис преобразования
Для операций между разными типами нужно явное приведение:
тип(значение)
Примеры приведения
int → float64:
var intNum int = 10
var floatNum float64 = 3.14
var result = float64(intNum) + floatNum
// result = 10.0 + 3.14 = 13.14 (тип float64)
float64 → int (потеря дробной части):
var result = intNum + int(floatNum)
// result = 10 + 3 = 13 (тип int, .14 отброшено!)
Важно: при приведении float к int дробная часть отбрасывается (не округляется):
var f float64 = 3.99
var i int = int(f) // i = 3 (не 4!)
Рекомендация по направлению конвертации
В большинстве случаев конвертируйте int → float64, чтобы не терять дробную часть:
// Правильно - сохраняем точность
var result = float64(intNum) + floatNum
// Неправильно - теряем дробную часть
var result = intNum + int(floatNum) // потеря .14
Автоматический вывод типов (Type Inference)
Компилятор умеет определять тип
Go может автоматически вывести тип из значения справа от =:
var inferredInt = 42 // компилятор выводит int
var inferredFloat = 3.14 // компилятор выводит float64
var inferredString = "Go" // компилятор выводит string
var inferredBool = true // компилятор выводит bool
Тип указывать не обязательно, если он очевиден из контекста.
Правила вывода типов
- Целое число без точки →
int - Число с точкой →
float64 - Строка в
""→string true/false→bool
fmt.Printf("%T\n", 42) // int
fmt.Printf("%T\n", 3.14) // float64
fmt.Printf("%T\n", "text") // string
fmt.Printf("%T\n", true) // bool
Оператор короткого объявления :=
Синтаксис
Краткая форма объявления и инициализации:
имя := значение
Эквивалентно:
var имя тип = значение // где тип выводится компилятором
Примеры использования
Полная форма vs краткая:
// Полная форма
var age int = 25
// Краткая форма (эквивалент)
age := 25
Автоматический вывод типа:
x := 42 // int
pi := 3.14159 // float64
name := "Иван" // string
isActive := true // bool
Ограничение: только внутри функций
Оператор := работает только внутри функций. На уровне пакета (вне функций) обязательно используйте var:
package main
// ВНЕ функции - только var
var globalVar int = 100
// globalVar := 100 // ОШИБКА! := работает только в функциях
func main() {
// ВНУТРИ функции - можно :=
localVar := 200 // OK
}
Когда использовать var vs :=
Используйте var когда:
1. На уровне пакета (обязательно):
package main
var PackageLevel int = 100 // обязательно var
2. Нужно явно указать тип:
var age int = 25 // явно показываем int
var timeout float64 = 30 // явно показываем float64
3. Объявление без инициализации:
var result int // объявили, но не присвоили
// ... код ...
result = calculateSomething() // присвоили позже
С := так нельзя — оно требует инициализации:
// result := // ОШИБКА! Нужно значение справа
4. Нулевое значение нужно явно:
var count int // 0
var message string // ""
var isReady bool // false
Используйте := когда:
1. Внутри функций для краткости (частый случай):
func main() {
name := "Сергей" // короче и читаемее
age := 25
isStudent := true
}
2. Тип очевиден из контекста:
x := 10
y := 20
sum := x + y // очевидно int
3. Множественное присваивание:
x, y := 12, 23
name, age := "Иван", 30
4. В циклах, условиях (будет в следующих уроках):
for i := 0; i < 10; i++ {
// i объявлена через :=
}
if err := doSomething(); err != nil {
// err объявлена через :=
}
Множественное объявление переменных
Множественное присваивание через :=
x, y := 12, 23
name, age := "Иван", 30
Эквивалентно:
var x int = 12
var y int = 23
Множественное объявление через var
Раздельно:
var x int = 12
var y int = 23
Вместе без типа:
var x, y = 12, 23 // компилятор выводит int
Вместе с типом:
var x, y int = 12, 23
Блоком:
var (
x int = 12
y int = 23
z int = 34
)
Ошибки повторного объявления
Нельзя объявлять дважды
Ошибка - два var:
var age int = 25
var age int = 30 // ОШИБКА! age redeclared
Ошибка - два :=:
age := 25
age := 30 // ОШИБКА! no new variables on left side of :=
Ошибка - var затем :=:
var age int
age := 25 // ОШИБКА! no new variables on left side of :=
Правильное переприсваивание
После объявления используйте = (без двоеточия):
var age int = 25
age = 30 // OK - просто присваивание
// или
age := 25
age = 30 // OK
Безопасность типов
Ошибки типов при компиляции
Go проверяет типы на этапе компиляции, а не во время выполнения:
func processNumber(n int) {
fmt.Printf("Число: %d\n", n)
}
func main() {
processNumber(42) // OK
// processNumber("42") // ОШИБКА компиляции
// processNumber(3.14) // ОШИБКА компиляции
}
Преимущество: баги с типами ловятся до запуска программы, а не в production.
Примеры ошибок компиляции
// cannot use "42" (type string) as type int in argument
processNumber("42")
// cannot use 3.14 (type float64) as type int in argument
processNumber(3.14)
// invalid operation: intNum + floatNum (mismatched types)
var result = intNum + floatNum
Практические примеры
Полный пример из урока
package main
import "fmt"
func main() {
// Тип фиксируется при объявлении
var number int = 42
fmt.Printf("number: %d, тип: %T\n", number, number)
// Можно изменить значение, но не тип
number = 100
fmt.Printf("После изменения: %d, тип: %T\n", number, number)
// ОШИБКА: нельзя изменить тип
// number = "строка" // cannot use "строка" (type string)
// number = 3.14 // cannot use 3.14 (type float64)
// Нет автоматического приведения
var intNum int = 10
var floatNum float64 = 3.14
// ОШИБКА: разные типы
// var result = intNum + floatNum
// Правильно: явное приведение
var result1 = float64(intNum) + floatNum // 13.14
var result2 = intNum + int(floatNum) // 13 (потеря .14)
fmt.Printf("float64 результат: %.2f\n", result1)
fmt.Printf("int результат: %d\n", result2)
// Краткая форма объявления
age := 25 // int
price := 99.99 // float64
name := "Сергей" // string
isActive := true // bool
fmt.Printf("age: %d (тип: %T)\n", age, age)
fmt.Printf("price: %.2f (тип: %T)\n", price, price)
fmt.Printf("name: %s (тип: %T)\n", name, name)
fmt.Printf("isActive: %t (тип: %T)\n", isActive, isActive)
// Множественное объявление
x, y := 12, 23
sum := x + y
fmt.Printf("%d + %d = %d\n", x, y, sum)
// Функция принимает только int
processNumber(42)
// processNumber("42") // ОШИБКА компиляции
// processNumber(3.14) // ОШИБКА компиляции
}
func processNumber(n int) {
fmt.Printf("Обработка числа: %d\n", n)
}
Важные моменты
1. Тип фиксируется навсегда
После объявления переменной с типом int она всегда будет int. Изменить тип невозможно.
2. Нет неявных преобразований
Go не преобразует типы автоматически. Всегда требуется явное float64(x) или int(y).
3. := только в функциях
На уровне пакета используйте только var. Оператор := работает исключительно внутри функций.
4. Потеря точности при int(float)
При приведении float к int дробная часть отбрасывается: int(3.99) = 3.
5. Вывод типа работает с обеими формами
var x = 42 // компилятор выводит int
x := 42 // то же самое
6. Нельзя переобъявлять
Переменную можно объявить только один раз. После этого используйте = для присваивания.
7. Безопасность на этапе компиляции Все ошибки типов ловятся компилятором до запуска программы — не нужно ждать runtime ошибок.
8. Стиль зависит от контекста
В профессиональных проектах часто предпочитают := внутри функций для краткости, но явный var с типом для важных переменных.
Что запомнить
- Go строго статически типизирован — тип фиксируется навсегда
- Нельзя присвоить переменной значение другого типа
- Нельзя смешивать типы без явного приведения
int(x),float64(y)— явное приведение типов- При
int(float)дробная часть теряется - Компилятор автоматически выводит типы из значений
:=— краткая форма объявления с инициализацией:=работает только внутри функцийvarобязательно на уровне пакетаx, y := 12, 23— множественное объявление- Нельзя переобъявлять переменную (повторный
:=илиvar) - После объявления используйте
=для присваивания - Ошибки типов ловятся при компиляции
- Предпочитайте
:=для краткости,varкогда нужна явность - Конвертируйте
int → float64для сохранения точности