Структуры и интерфейсы¶
TL;DR¶
- struct группирует поля.
- Методы привязываются через receiver:
func (u *User) Foo(). - Pointer receiver для модификации; value — для read-only мелочей.
- Интерфейс — implicit, реализация по структуре методов.
any(он жеinterface{}) — может хранить любой тип.
Структуры¶
type User struct {
ID int64
Name string
Age int
}
u := User{ID: 1, Name: "Anna", Age: 30}
u2 := User{1, "Bob", 25} // позиционно — не рекомендуется
Сравнение: структуры comparable если все поля comparable.
Методы¶
type Counter struct{ n int }
func (c Counter) Get() int { return c.n } // value receiver
func (c *Counter) Inc() { c.n++ } // pointer receiver
Правило: не миксуй receiver'ы у одного типа. Либо все pointer, либо все value. Иначе можешь случайно потерять изменения.
Интерфейсы¶
В Go реализация интерфейса implicit — не нужно объявлять implements.
type Stringer interface {
String() string
}
type User struct{ Name string }
func (u User) String() string { return u.Name }
var s Stringer = User{Name: "Anna"} // работает автоматически
Принцип маленьких интерфейсов¶
В стандартной библиотеке интерфейсы маленькие: io.Reader, io.Writer,
io.Closer. Большие интерфейсы → много реализаций ломается.
«Accept interfaces, return structs» — функции принимают интерфейс (минимальный нужный), возвращают конкретные структуры.
Гочча — typed nil¶
type MyError struct{}
func (e *MyError) Error() string { return "" }
func doSomething() error {
var p *MyError = nil
return p
}
err := doSomething()
fmt.Println(err == nil) // false!
Объяснение: интерфейс хранит (type, value). type = *MyError (≠ nil).
err == nil истинно только когда type=nil И value=nil.
Правильно:
func doSomething() error {
var p *MyError
if shouldFail() {
p = &MyError{}
}
if p == nil {
return nil // явно вернуть nil, не *MyError(nil)
}
return p
}
📖 Связанные задачи: must-solve-100 → C-009..C-012.