Переменные и типы данных
Объявление переменных
Go предоставляет несколько способов объявления переменных. Каждый способ уместен в определённом контексте.
Полное объявление с var
package main
import "fmt"
func main() {
// Explicit type, assigned later
var name string
name = "Go"
// Explicit type with initialization
var age int = 15
// Type inferred from value
var language = "Golang"
// Multiple variables of the same type
var x, y, z int
x, y, z = 1, 2, 3
// Multiple variables with different types (block form)
var (
host string = "localhost"
port int = 8080
debug bool = false
version = "1.0.0" // type inferred as string
)
fmt.Println(name, age, language, x, y, z)
fmt.Println(host, port, debug, version)
}
Краткое объявление :=
Оператор := объявляет и инициализирует переменную одновременно. Тип выводится автоматически.
func main() {
// Short variable declaration (type inferred)
name := "Go"
count := 42
pi := 3.14159
isReady := true
// Multiple short declarations
a, b, c := 1, "hello", true
// At least one variable must be NEW on the left side
a, d := 10, 20 // 'a' is reassigned, 'd' is new — OK
_ = d
fmt.Println(name, count, pi, isReady, a, b, c)
}
Важно: Оператор
:=можно использовать только внутри функций. На уровне пакета допускается толькоvar.
Сравнение способов объявления
| Способ | Где используется | Пример |
|---|---|---|
var x T |
Когда нулевое значение достаточно | var count int |
var x T = v |
Когда тип и значение явные | var count int = 10 |
var x = v |
На уровне пакета с выводом типа | var name = "Go" |
x := v |
Внутри функций (самый частый) | count := 10 |
Правила именования
package main
// Exported (public) — starts with uppercase
var MaxRetries = 3
// Unexported (private to package) — starts with lowercase
var defaultTimeout = 30
func main() {
// Local variable — short, descriptive
i := 0 // loop counter — short is fine
user := getUser() // clear meaning
db := openDB() // common abbreviation
// Avoid stuttering in package context
// user.User — BAD
// user.Profile — GOOD
_ = i
_ = user
_ = db
}
Правила именования переменных в Go:
- Начинается с буквы или
_ - Может содержать буквы, цифры,
_ - Регистрозависимые (
nameиName— разные переменные) Name(с заглавной) — экспортируемое имя (видно за пределами пакета)name(со строчной) — неэкспортируемое имя
Базовые типы
Целочисленные типы
| Тип | Размер | Диапазон |
|---|---|---|
int8 |
8 бит | -128 ... 127 |
int16 |
16 бит | -32768 ... 32767 |
int32 |
32 бит | -2,147,483,648 ... 2,147,483,647 |
int64 |
64 бит | -9.2 * 10^18 ... 9.2 * 10^18 |
uint8 |
8 бит | 0 ... 255 |
uint16 |
16 бит | 0 ... 65535 |
uint32 |
32 бит | 0 ... 4,294,967,295 |
uint64 |
64 бит | 0 ... 18.4 * 10^18 |
int |
32 или 64 бит | Зависит от платформы |
uint |
32 или 64 бит | Зависит от платформы |
uintptr |
Размер указателя | Для хранения адресов |
package main
import (
"fmt"
"math"
)
func main() {
// Platform-dependent int (64-bit on modern systems)
var count int = 42
// Fixed-size integers
var small int8 = 127
var medium int32 = math.MaxInt32
var large int64 = math.MaxInt64
// Unsigned integers
var positive uint = 100
var byte_ uint8 = 255
// Integer literals in different bases
decimal := 42
binary := 0b101010 // Binary (Go 1.13+)
octal := 0o52 // Octal with prefix (Go 1.13+)
octalOld := 052 // Octal (old style)
hex := 0x2A // Hexadecimal
// Underscore separator for readability (Go 1.13+)
billion := 1_000_000_000
hexColor := 0xFF_FF_FF
fmt.Println(count, small, medium, large, positive, byte_)
fmt.Println(decimal, binary, octal, octalOld, hex)
fmt.Println(billion, hexColor)
}
Числа с плавающей точкой
| Тип | Размер | Точность |
|---|---|---|
float32 |
32 бит | ~7 значащих цифр |
float64 |
64 бит | ~15 значащих цифр |
complex64 |
64 бит | float32 + float32 |
complex128 |
128 бит | float64 + float64 |
func main() {
// float64 is the default for floating point literals
pi := 3.14159265358979
var f32 float32 = 3.14
// Scientific notation
avogadro := 6.022e23
planck := 6.626e-34
// Complex numbers
c1 := complex(3, 4) // 3+4i
c2 := 2 + 3i // Literal syntax
realPart := real(c1) // 3
imagPart := imag(c1) // 4
fmt.Println(pi, f32, avogadro, planck)
fmt.Println(c1, c2, realPart, imagPart)
}
Совет: Всегда используйте
float64, если нет веской причины использоватьfloat32. Литералы с плавающей точкой в Go по умолчанию имеют типfloat64.
Логический тип
func main() {
var isReady bool // Zero value: false
isActive := true
isEmpty := false
// Boolean operations
result := isActive && !isEmpty // AND, NOT
either := isActive || isEmpty // OR
// Comparison results are bool
isEqual := 5 == 5 // true
isGreater := 10 > 5 // true
fmt.Println(isReady, isActive, result, either, isEqual, isGreater)
}
Строки и символы
package main
import (
"fmt"
"strings"
"unicode/utf8"
)
func main() {
// Strings are immutable sequences of bytes (UTF-8 encoded)
greeting := "Hello, Мир!"
raw := `This is a raw string literal.\n No escape sequences.`
// String length
byteLen := len(greeting) // Byte length (not rune count!)
runeLen := utf8.RuneCountInString(greeting) // Character count
fmt.Printf("Bytes: %d, Runes: %d\n", byteLen, runeLen)
// Output: Bytes: 14, Runes: 11 (Cyrillic chars take 2 bytes)
// Byte vs Rune
var b byte = 'A' // byte is alias for uint8
var r rune = 'Я' // rune is alias for int32 (Unicode code point)
fmt.Printf("byte: %d (%c), rune: %d (%c)\n", b, b, r, r)
// Iterating over strings
for i, ch := range greeting {
fmt.Printf("index=%d, rune=%c, unicode=%U\n", i, ch, ch)
}
// String building (efficient)
var sb strings.Builder
for i := 0; i < 100; i++ {
sb.WriteString("Go ")
}
result := sb.String()
_ = result
_ = raw
}
| Тип | Размер | Описание |
|---|---|---|
byte |
8 бит | Алиас для uint8, один байт |
rune |
32 бит | Алиас для int32, кодовая точка Unicode |
string |
16 байт (header) | Неизменяемая последовательность байт |
Нулевые значения (Zero Values)
В Go каждый тип имеет нулевое значение по умолчанию. Неинициализированные переменные всегда имеют определённое значение — нет понятия «мусора в памяти».
| Тип | Нулевое значение |
|---|---|
int, int8...int64 |
0 |
uint, uint8...uint64 |
0 |
float32, float64 |
0.0 |
complex64, complex128 |
(0+0i) |
bool |
false |
string |
"" (пустая строка) |
byte (uint8) |
0 |
rune (int32) |
0 |
Указатель (*T) |
nil |
Слайс ([]T) |
nil |
Карта (map[K]V) |
nil |
Канал (chan T) |
nil |
| Интерфейс | nil |
| Функция | nil |
| Структура | Все поля имеют свои нулевые значения |
func main() {
var (
i int
f float64
b bool
s string
p *int
sl []int
m map[string]int
ch chan int
fn func()
)
fmt.Printf("int: %d\n", i) // 0
fmt.Printf("float64: %f\n", f) // 0.000000
fmt.Printf("bool: %t\n", b) // false
fmt.Printf("string: %q\n", s) // ""
fmt.Printf("pointer: %v\n", p) // <nil>
fmt.Printf("slice: %v\n", sl) // []
fmt.Printf("map: %v\n", m) // map[]
fmt.Printf("channel: %v\n", ch) // <nil>
fmt.Printf("function: %v\n", fn) // <nil>
}
Преобразование типов
Go не имеет неявного преобразования типов (implicit conversion). Все преобразования явные.
package main
import (
"fmt"
"strconv"
)
func main() {
// Numeric conversions (explicit only!)
var i int = 42
var f float64 = float64(i) // int -> float64
var u uint = uint(f) // float64 -> uint
// This will NOT compile:
// var f float64 = i // ERROR: cannot use i (type int) as type float64
// Precision loss is silent!
bigFloat := 3.999
truncated := int(bigFloat) // 3 (truncated, NOT rounded)
// int8 overflow (wraps around)
big := 256
small := int8(big) // 0 (overflow!)
fmt.Println(i, f, u, truncated, small)
// String conversions
numStr := strconv.Itoa(42) // int -> string: "42"
num, err := strconv.Atoi("42") // string -> int: 42
if err != nil {
fmt.Println("conversion error:", err)
}
// float <-> string
floatStr := strconv.FormatFloat(3.14, 'f', 2, 64) // "3.14"
floatVal, _ := strconv.ParseFloat("3.14", 64) // 3.14
// bool <-> string
boolStr := strconv.FormatBool(true) // "true"
boolVal, _ := strconv.ParseBool("true") // true
// String <-> []byte
s := "Hello"
bytes := []byte(s) // String to byte slice
s2 := string(bytes) // Byte slice to string
// String <-> []rune
runes := []rune("Привет") // String to rune slice
s3 := string(runes) // Rune slice to string
// int -> string: converts to Unicode character, NOT number!
// string(65) == "A" (Unicode code point 65)
// Use strconv.Itoa(65) for "65"
fmt.Println(numStr, num, floatStr, floatVal, boolStr, boolVal)
fmt.Println(s2, s3)
}
Ловушка:
string(65)вернёт"A"(символ с кодом 65), а НЕ"65". Для числа в строку используйтеstrconv.Itoa()илиfmt.Sprintf().
Псевдонимы типов и определения типов
package main
import "fmt"
// Type definition — creates a NEW type
type Celsius float64
type Fahrenheit float64
type UserID int64
// Type alias — same type, different name (Go 1.9+)
type Float = float64 // Float IS float64
func main() {
var temp Celsius = 36.6
var tempF Fahrenheit = 97.88
// Cannot mix defined types without conversion!
// temp = tempF // ERROR: cannot use tempF (Fahrenheit) as Celsius
// Explicit conversion required
temp = Celsius(tempF)
// Type alias works without conversion
var f Float = 3.14
var f64 float64 = f // OK: Float IS float64
_ = f64
// Defined types can have methods
fmt.Printf("Temperature: %.1f°C\n", temp)
id := UserID(12345)
fmt.Printf("User ID: %d\n", id)
}
// Methods on defined types
func (c Celsius) ToFahrenheit() Fahrenheit {
return Fahrenheit(c*9/5 + 32)
}
func (f Fahrenheit) ToCelsius() Celsius {
return Celsius((f - 32) * 5 / 9)
}
| Механизм | Синтаксис | Новый тип? | Методы? |
|---|---|---|---|
| Type definition | type X T |
Да | Да |
| Type alias | type X = T |
Нет | Нет (наследуются) |
Константы
Объявление констант
package main
import "fmt"
// Simple constants
const Pi = 3.14159265358979
const E = 2.71828
// Block declaration
const (
AppName = "MyApp"
AppVersion = "1.0.0"
MaxRetries = 3
)
// Typed constants
const TypedPi float64 = 3.14
// Untyped constants — more flexible
const UntypedPi = 3.14 // Can be used where float32 or float64 is expected
func main() {
// Untyped constant adapts to context
var f32 float32 = UntypedPi // OK: untyped adapts to float32
var f64 float64 = UntypedPi // OK: untyped adapts to float64
// var f32typed float32 = TypedPi // ERROR: TypedPi is float64
fmt.Println(f32, f64)
// Constants must be known at compile time
// const x = someFunction() // ERROR: not a constant expression
}
Нетипизированные константы
Нетипизированные константы в Go обладают произвольной точностью и адаптируются к контексту:
const (
// This number is too large for any integer type,
// but works as an untyped constant
Huge = 1 << 100
// Can be used in expressions with other constants
Half = Huge / 2
)
func main() {
// Huge cannot be stored in any variable directly
// var x int = Huge // ERROR: overflows int
// But it works in constant expressions
fmt.Println(Huge / (1 << 90)) // 1024
}
iota — генератор последовательностей
iota — специальный идентификатор, который используется в блоках const для генерации последовательных значений:
package main
import "fmt"
// Simple enumeration (starts at 0)
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
// Start from 1
const (
January = iota + 1 // 1
February // 2
March // 3
)
// Bitmask pattern (powers of 2)
const (
ReadPermission = 1 << iota // 1 (1 << 0)
WritePermission // 2 (1 << 1)
ExecutePermission // 4 (1 << 2)
)
// Skip values with blank identifier
const (
_ = iota // Skip 0
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1,048,576
GB // 1 << 30
TB // 1 << 40
)
// Custom string representation
type Color int
const (
Red Color = iota // 0
Green // 1
Blue // 2
)
func (c Color) String() string {
names := [...]string{"Red", "Green", "Blue"}
if c < Red || c > Blue {
return "Unknown"
}
return names[c]
}
func main() {
fmt.Println(Sunday, Monday, Saturday) // 0 1 6
fmt.Println(January, February, March) // 1 2 3
fmt.Println(ReadPermission, WritePermission) // 1 2
// Combining permissions with bitwise OR
perm := ReadPermission | WritePermission
fmt.Printf("Permissions: %03b\n", perm) // 011
// Check permission with bitwise AND
hasRead := perm&ReadPermission != 0
fmt.Println("Has read:", hasRead) // true
fmt.Println(KB, MB, GB) // 1024 1048576 1073741824
fmt.Println(Red, Green, Blue) // Red Green Blue
}
Важно:
iotaсбрасывается в0в каждом новом блокеconst. Внутри блокаiotaувеличивается на 1 для каждой строки (даже если строка пустая или содержит комментарий).
Перечисления (Enums) в Go
Go не имеет встроенных enum, но iota с определённым типом создаёт аналогичный паттерн:
type Status int
const (
StatusPending Status = iota // 0
StatusActive // 1
StatusInactive // 2
StatusDeleted // 3
)
// Validate checks if status is valid
func (s Status) Validate() bool {
return s >= StatusPending && s <= StatusDeleted
}
// IsActive checks if status means active
func (s Status) IsActive() bool {
return s == StatusActive
}