
- Главная
- Каталог
- Интернет технологии
- Golang задачи и тесты
Статистика канала
package main
import (
"fmt"
"unsafe"
)
type Foo struct {
A int64
}
type IFoo interface {
Get() int64
}
func (f Foo) Get() int64 { return f.A }
func main() {
var a int64 = 10
var s Foo = Foo{A: 42}
var i IFoo = s
fmt.Println(unsafe.Sizeof(a))
fmt.Println(unsafe.Sizeof(s))
fmt.Println(unsafe.Sizeof(i))
}
{}
Вопросы
• Сколько байт занимает a на x64?
• Сколько байт занимает s?
• Сколько байт занимает i?
• Почему размер i не равен размеру Foo?
• Что реально хранится внутри interface{} в Go?
Разберём по шагам, как это работает в Go на x64.
Код:
```go
var a int64 = 10
var s Foo = Foo{A: 42}
var i IFoo = s
```
1) Размер a
unsafe.Sizeof(a)
```go
int64 = 8 байт
```
На x64:
a = 8 байт
2) Размер s
```go
type Foo struct {
A int64
}```
В структуре одно поле int64.
```go
s = 8 байт
```
3) Размер интерфейса i
```go
var i IFoo = s
unsafe.Sizeof(i)
```go
На x64:
```go
i = 16 байт```
Запустить код: https://go.dev/play/p/NLIG3q-jFc9nil - это не просто «пусто».
Иногда это причина самых странных багов в продакшене.
nil в Go - это не одно значение, а множество разных nil.
1. Interface и nil — ловушка
var err error = nil
fmt.Println(err == nil) // true
var e *MyError = nil
var err error = e
fmt.Println(err == nil) // false (!)
{}
Почему так?
Интерфейс в Go хранит:
• тип
• значение
В этом случае:
• тип = *MyError
• значение = nil
Интерфейс не nil, потому что тип задан.
Именно так появляются ошибки вида:
if err != nil срабатывает
но внутри на самом деле nil
2. Typed nil vs untyped nil
var p *int = nil
var s []int = nil
var m map[string]int = nil
{}
Все они nil, но разных типов.
И Go сравнивает не только значение, но и тип.
3. Panic из-за nil внутри interface
type User struct{}
func (u *User) Name() string {
return "John"
}
var u *User = nil
var i interface{} = u
i.(*User).Name() // panic
{}
Интерфейс не nil → метод вызывается → внутри nil → panic.
4. Частый баг с error
Плохой код:
func do() error {
var e *MyError = nil
return e
}
{}
Правильно:
func do() error {
if somethingWrong {
return &MyError{}
}
return nil
}
{}
Главное правило
Если работаешь с interface:
• всегда проверяй, что возвращаешь чистый nil
помни:
interface != nil, если внутри typed nil
{}
https://sushantdhiman.dev/nil-in-go-is-not-what-you-think/int64 требует выравнивания по 8 байтам
- int32 - по 4 байтам
- byte - по 1 байту
Если значение лежит “неудобно”, CPU делает несколько операций вместо одной - это медленнее.
Почему появляется padding
Компилятор Go автоматически вставляет пустые байты (padding) между полями структуры, чтобы соблюсти правила выравнивания.
Размер структуры - это не просто сумма размеров полей.
Пример:
type Example struct {
a byte // 1 байт
b int64 // 8 байт (нужно выравнивание по 8)
c byte // 1 байт
}{}
Хотя “полезных” данных тут 10 байт, структура занимает больше из-за вставленного паддинга.
Почему это важно
Если структура используется миллионы раз (в кэше, массивах, базах, сетевых пакетах), лишние байты превращаются в:
• большее потребление памяти
• худшее попадание в CPU cache
• падение производительности
Как оптимизировать
• Порядок полей имеет значение.
• Крупные типы лучше ставить первыми:
type Optimized struct {
b int64
a byte
c byte
}{}
Так компилятору нужно меньше padding, и структура становится компактнее.
Главное правило
В Go порядок полей в структуре влияет на производительность.
Выравнивание памяти - это не микроскопическая оптимизация.
В высоконагруженных системах это даёт реальный прирост скорости и экономию памяти.
Сохрани себе - пригодится при проектировании структур данных.
Больше примеровBuffer.Peek(n) в пакете bytes.
Что делает:
- возвращает следующие N байт
- не сдвигает указатель чтения
- то есть можно “подсмотреть”, что впереди, и не портить поток
Это идеальная штука для:
- парсеров протоколов
- stream processing
- проверок заголовков/сигнатур
- peek-before-read логики
Важный нюанс:
Peek() возвращает срез, который указывает прямо на внутренний буфер.
То есть:
- он валиден, пока буфер не изменился
- если ты поменяешь срез - поменяешь буфер (осторожно!)
Мини-юзкейс: сначала peek, потом решаешь - читать дальше или нет.
package main
import "fmt"
func main() {
s := make([]int, 2, 3)
s[0], s[1] = 1, 2
a := s[:2]
b := append(s, 3)
modify(a)
modify(b)
fmt.Println(s)
fmt.Println(a)
fmt.Println(b)
}
func modify(x []int) {
x = append(x, 100)
x[0] = 999
}
{}
- Какие append используют тот же underlying array, а какие нет?
- В какой момент происходит реаллокация памяти?
Подсказка:
s, a и b сначала делят один backing array,
но не все изменения доходят до всех слайсов.
Ответ:
[999 2]
[999 2]
[999 2 100]
📌 Запустить код: https://go.dev/play/p/wVRz6QFmMCvОтзывы канала
всего 2 отзыва
- Добавлен: Сначала новые
- Добавлен: Сначала старые
- Оценка: По убыванию
- Оценка: По возрастанию
Каталог Телеграм-каналов для нативных размещений
Golang задачи и тесты — это Telegam канал в категории «Интернет технологии», который предлагает эффективные форматы для размещения рекламных постов в Телеграмме. Количество подписчиков канала в 7.8K и качественный контент помогают брендам привлекать внимание аудитории и увеличивать охват. Рейтинг канала составляет 11.8, количество отзывов – 2, со средней оценкой 5.0.
Вы можете запустить рекламную кампанию через сервис Telega.in, выбрав удобный формат размещения. Платформа обеспечивает прозрачные условия сотрудничества и предоставляет детальную аналитику. Стоимость размещения составляет 8251.74 ₽, а за 46 выполненных заявок канал зарекомендовал себя как надежный партнер для рекламы в TG. Размещайте интеграции уже сегодня и привлекайте новых клиентов вместе с Telega.in!
Вы снова сможете добавить каналы в корзину из каталога
Комментарий