Урок 4~12 минут
Структуры
Объявление структуры
go
type Person struct {
Name string
Age int
Email string
}Создание
go
// Именованный литерал — предпочтительно
p := Person{
Name: "Алиса",
Age: 30,
Email: "alice@example.com",
}
// Позиционный — хрупко, избегай
p2 := Person{"Боб", 25, "bob@example.com"}
// Нулевая структура
var p3 Person
fmt.Println(p3.Name) // ""
fmt.Println(p3.Age) // 0
// Указатель через new
p4 := new(Person) // *Person, все поля = zero value
p5 := &Person{Name: "Карл"} // *Person с инициализациейМетоды
Методы — функции с получателем (receiver):
go
func (p Person) Greet() string {
return fmt.Sprintf("Привет, я %s!", p.Name)
}
func (p *Person) Birthday() {
p.Age++
}
alice := Person{Name: "Алиса", Age: 30}
fmt.Println(alice.Greet()) // Привет, я Алиса!
alice.Birthday()
fmt.Println(alice.Age) // 31Value receiver (p Person) — получает копию, не может изменить оригинал.
Pointer receiver (p *Person) — получает указатель, изменения видны снаружи.
Правило: если хотя бы один метод требует pointer receiver — делай все методы pointer receiver.
Конструкторы
В Go нет new как в C++/Java. Конструктор — просто функция:
go
func NewPerson(name string, age int) *Person {
if age < 0 {
age = 0
}
return &Person{
Name: name,
Age: age,
}
}
alice := NewPerson("Алиса", 30)Возвращай *T — дешевле копирования, и позволяет вернуть nil при ошибке.
Встраивание (Embedding)
Встраивание — «наследование» методов без наследования классов:
go
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return a.Name + " говорит"
}
type Dog struct {
Animal // встроен без имени поля
Breed string
}
d := Dog{
Animal: Animal{Name: "Рекс"},
Breed: "Хаски",
}
fmt.Println(d.Speak()) // Рекс говорит — метод Animal доступен напрямую
fmt.Println(d.Name) // Рекс — поле Animal тожеDog может переопределить Speak():
go
func (d Dog) Speak() string {
return d.Name + " лает: Гав!"
}Struct tags
Теги — метаданные, читаемые через reflection. Используются encoding/json, ORM-библиотеками, валидаторами:
go
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email,omitempty"`
Password string `json:"-"` // никогда не сериализовать
}
u := User{ID: 1, Name: "Алиса", Email: "alice@example.com"}
data, _ := json.Marshal(u)
fmt.Println(string(data))
// {"id":1,"name":"Алиса","email":"alice@example.com"}omitempty— пропустить если поле пустое-— всегда пропускать
Анонимные структуры
go
// Одноразовые структуры — не нужно объявлять тип
point := struct {
X, Y int
}{X: 10, Y: 20}
// Полезно в тестах
tests := []struct {
input int
expected int
}{
{2, 4},
{3, 9},
{4, 16},
}Сравнение структур
Структуры сравниваемы если все поля comparable:
go
type Point struct{ X, Y int }
p1 := Point{1, 2}
p2 := Point{1, 2}
p3 := Point{3, 4}
fmt.Println(p1 == p2) // true
fmt.Println(p1 == p3) // falseСтруктуры со слайсами или map — не сравниваемы через ==.
Структура с методом-получателем — это почти объект. Но в Go нет наследования, только встраивание (embedding).
ОРИГИНАЛ В ПАМЯТИ
person (0x0042)
Name:"Алиса"
Age:30
КОПИЯ (только при value receiver)
p (копия на стеке)
Name:"Алиса"
Age:?
копии нет
Value receiver (p Person)
• Получает КОПИЮ структуры
• Оригинал не изменяется
• Подходит для чтения данных
• Меньше накладных расходов для маленьких структур
Pointer receiver (p *Person)
• Получает УКАЗАТЕЛЬ на оригинал
• Может изменять оригинал
• Нужен для мутирующих методов
• Обязателен для больших структур (экономия копирования)
🎯
Миссия 1 из 4
В чём разница между value receiver и pointer receiver?