TGViewer
Channel Public Channel
Go tests

Go tests

@golangtests

По всем вопросам- @haarrp

@itchannels_telegram - 🔥полезные ит-каналы

https://t.me/Golang_google - Golang программирование

@golangl - golang chat

@GolangJobsit - golang channel jobs

@golang_jobsgo - go chat jobs
Subscribers
7.84K
Photos
317
Videos
6
Links
115

Showing posts older than #903 · Back to latest

Older Posts 20 shown
Post #901 3.12K
🔥 PrimeView - реальная производительность языков без маркетинга

https://plummerssoftwarellc.github.io/PrimeView/report

PrimeView - это живой бенчмарк, который сравнивает языки программирования на одной задаче: генерация простых чисел.

Без синтетических тестов.
Без “идеальных условий”.
Только:

* один алгоритм
* одинаковые правила
* реальные оптимизации от разработчиков

Что показывают результаты:

* C и C++ стабильно в лидерах
* Rust и Zig уверенно конкурируют с нативным кодом
* Go показывает сильные результаты при грамотной оптимизации
* разница между реализациями внутри одного языка часто больше, чем между языками

Ключевой инсайт:

Производительность определяет не язык.
Производительность определяет инженер.

SIMD, работа с памятью, кэш-локальность и битовые оптимизации дают больший эффект, чем выбор технологии.

plummerssoftwarellc.github.io/PrimeView/report
  • 👍 4
  • 🔥 3
  • 🥰 1
Post #899 3.21K
В Go nil - это не просто «пусто».
Иногда это причина самых странных багов в продакшене.

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/
  • ❤ 6
  • 👍 5
  • 🔥 4
Post #898 3.36K
Это один из самых любимых вопросов на Go-собеседованиях.
Проверяют, понимаешь ли ты, как устроены slice, len / cap и что реально делает append.

Что выведет программа?


package main

import "fmt"

func add(s []int) {
s = append(s, 99)
}

func main() {
a := make([]int, 2, 4)
a[0], a[1] = 1, 2

add(a)

fmt.Println("a:", a)
fmt.Println("a[:3]:", a[:3])
}


Разбор

```go
a := make([]int, 2, 4)
len = 2
cap = 4
underlying array = [1, 2, _, _]
```

Слайс - это структура:
```go
pointer -> array
len
cap
```

При передаче в функцию копируется заголовок, но указывает он на тот же массив.

Внутри:
```go
s = append(s, 99)
```

Так как cap=4, места хватает → новый массив НЕ создаётся.
В память записывается:

```go
[1, 2, 99, _]
```

Но:

s внутри функции: len=3

```go
a в main: len=2
```

Вывод
```go
a: [1 2]
a[:3]: [1 2 99]
```

Вопрос 2 (сложнее)
package main



import "fmt"

func add(s []int) {
s = append(s, 99)
s[0] = 42
}

func main() {
a := []int{1, 2}
add(a)

fmt.Println("a:", a)
}


Разбор
```go
a := []int{1,2}
len=2
cap=2
```

Теперь в append места нет → создаётся новый массив.

Внутри add:
```go
new array = [1 2 99]
s -> указывает на новый массив
```

Но a всё ещё указывает на старый:

```go
old array = [1 2]
```

Строка:

```go
s[0] = 42
```

Меняет новый массив, не старый.

Вывод
```go
a: [1 2]
```

Главная идея

Поведение append зависит от capacity.

Условие Что происходит
cap хватает меняется тот же массив
cap не хватает создаётся новый

Slice передаётся по значению, но содержит указатель на массив.
append может незаметно "оторвать" его от исходных данных.

Как писать безопасно
```go
func add(s []int) []int {
s = append(s, 99)
return s
}

a = add(a)
```

Что этим проверяют на собесе

• slice ≠ массив
• len vs cap
• устройство slice header
• поведение append
• aliasing памяти

Если это не понимать, будут странные баги в продакшене.


Запустить код: https://go.dev/play/p/V9KyryLj3TV
  • 👍 15
  • ❤ 7
  • 🔥 5
  • 👎 1
Post #896 3.16K
👣 Понимание выравнивания памяти в Go

В Go важно понимать, как компилятор размещает данные в памяти - это напрямую влияет на скорость работы и объём используемой памяти.

Что такое выравнивание?

Выравнивание памяти - это правило: данные должны находиться по адресам, кратным их размеру. Это нужно, чтобы процессор мог читать и записывать значения за одну операцию.

Например:
- 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 порядок полей в структуре влияет на производительность.

Выравнивание памяти - это не микроскопическая оптимизация.
В высоконагруженных системах это даёт реальный прирост скорости и экономию памяти.

Сохрани себе - пригодится при проектировании структур данных.

Больше примеров
  • 👍 21
  • ❤ 7
  • 🔥 4
Post #895 3.55K
👣 Go 1.26: `bytes.Buffer.Peek()` - заглянуть в буфер без чтения

В Go 1.26 добавили очень удобный метод: Buffer.Peek(n) в пакете bytes.

Что делает:
- возвращает следующие N байт
- не сдвигает указатель чтения
- то есть можно “подсмотреть”, что впереди, и не портить поток

Это идеальная штука для:
- парсеров протоколов
- stream processing
- проверок заголовков/сигнатур
- peek-before-read логики

Важный нюанс:
Peek() возвращает срез, который указывает прямо на внутренний буфер.
То есть:
- он валиден, пока буфер не изменился
- если ты поменяешь срез - поменяешь буфер (осторожно!)

Мини-юзкейс: сначала peek, потом решаешь - читать дальше или нет.
  • 👍 13
  • ❤ 4
  • 🔥 3
Post #894 3.43K
👣 Что выведет программа построчно?


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
  • ❤ 10
  • 👍 5
  • 🔥 3
Post #893 3.47K
🎄 Новогодняя елка на Golang 🎁

Как быстро быстро вывести новогоднюю ёлку в консоль, используя вложенные циклы и простую работу со строками.

Отличный пример для тренировки логики и работы с циклами. Подписывайся, больше фишек каждый день !


package main

import "fmt"

func main() {
height := 8
for i := 1; i <= height; i++ {
// пробелы
for s := 0; s < height-i; s++ {
fmt.Print(" ")
}
// ветви
for j := 0; j < 2*i-1; j++ {
fmt.Print("*")
}
fmt.Println()
}
// ствол
for i := 0; i < 2; i++ {
for s := 0; s < height-1; s++ {
fmt.Print(" ")
}
fmt.Println("|")
}
}
  • ❤ 6
  • 👍 6
  • 👎 2
  • 😁 2
Post #892 3.57K
🚀 Как Go 1.26 ловит утечки горутин

Перед тобой классический пример утечки goroutine, который долгое время было сложно отлавливать автоматически.

Проблема возникает, когда:
- используются небуферизированные каналы
- запускаются goroutine в цикле
- происходит ранний return по ошибке

В такой ситуации часть goroutine продолжает попытки записи в канал, но получателя уже нет — они зависают навсегда.

Раньше такие баги:
- проявлялись только под нагрузкой
- маскировались как рост памяти или странные тайминги
- находились уже в продакшене

В Go 1.26 runtime научился детектить такие утечки автоматически, если включено профилирование.


func fetchUsers(ids []int) ([]*User, error) {
ch := make(chan *User)

for _, id := range ids {
go func(id int) {
user := fetchUser(id)
ch <- user // блокируется навсегда при раннем return
}(id)
}

for range ids {
user := <-ch
if user == nil {
return nil, errors.New("failed") // LEAK: другие goroutine зависли
}
}
return nil, nil
}

Запуск с детекцией утечек goroutine
GOEXPERIMENT=goroutineleakprofile go run main.go

Проверка профиля
curl http://localhost:6060/debug/pprof/goroutineleak



https://www.youtube.com/shorts/Wvimjygw_ys
  • 👍 11
  • 🔥 4
  • ❤ 3
Post #891 2.85K
❓ Что выведет программа?

И объясни, почему вывод - НЕ тот, который ожидает большинство 🙂



package main

import "fmt"

func main() {
s := []int{1, 2, 3, 4}
t := s[:2] // [1 2]
u := s[2:] // [3 4]

// Изменяем через t
t = append(t, 9)

// Изменяем через u
u[0] = 99

fmt.Println("s:", s)
fmt.Println("t:", t)
fmt.Println("u:", u)
}


🔥 Разбор

t := s[:2] и u := s[2:] смотрят на один и тот же underlying array

но append(t, 9) может либо
• дописать в тот же массив, либо
• выделить новый, если capacity закончилась

Именно тут начинается путаница:

если capacity исходного s >= 5 — append изменит исходный массив, и s, t, u будут видеть изменения друг друга

если capacity = 4 — append создаст новый backing array, и t “оторвётся” от s и u

То есть вывод зависит от capacity, а её большинство разработчиков не проверяют, но предполагают.

✔️ Добавим интриги — сделай capacity явной 👇
s := make([]int, 4, 4) // capacity = length
copy(s, []int{1,2,3,4})


Теперь:

- append(t, 9) создаёт новый массив

и t уже не связан с s

Вывод будет:

```
s: [1 2 99 4]
t: [1 2 9]
u: [99 4]

```
Но если capacity увеличить:

```go
s := make([]int, 4, 10) // capacity > length
copy(s, []int{1,2,3,4})

```

Мы получим:


```
s: [1 2 9 99]
t: [1 2 9]
u: [99 4]

```

Потому что в этот раз append записал в тот же underlying array.


В Go поведение slice зависит от capacity --и два, казалось бы, одинаковых куска кода могут работать по-разному без единой строки изменения.


👉 Запустить код
  • ❤ 6
  • 👍 3
  • 👎 3
  • 😱 1
Post #889 2.25K
❓ Что выведет программа?


package main

import (
"fmt"
)

type S struct {
v int
}

func (s S) Value() int {
return s.v
}

type I interface {
Value() int
}

func mutate(i I) {
// Мы думаем, что меняем реальный объект...
if s, ok := i.(S); ok {
s.v = 100
}
}

func main() {
s := S{v: 10}
var i I = s

mutate(i)

fmt.Println(s.v) // ???
fmt.Println(i.Value()) // ???
}


Всё выглядит так:

Мы передаём объект в mutate → внутри меняем → значения должны измениться.

Тут ловушка в том, что:

- интерфейсы в Go копируют значение, если оно не pointer receiver

- type assertion s, ok := i.(S) создаёт новую копию

- мы мутируем копию, а не оригинал

Так что вывод будет:

10
10

А не 100.

👉 Запустить код
  • 👍 11
  • 🔥 4
  • ❤ 2
Post #887 2.51K
👣 Что выйдете код ?



package main

import "fmt"

func main() {
s := []int{1, 2, 3}
t := s // t и s смотрят на один и тот же массив
u := append(s, 4)

s[0] = 99 // меняем через s
t = append(t, 5)

fmt.Println("s:", s)
fmt.Println("t:", t)
fmt.Println("u:", u)
}


Ответ:
s: [99 2 3]
t: [99 2 3 5]
u: [1 2 3 4]


Дополнительные Вопросы : Где именно произойдёт перераспределение массива, какие срезы продолжат делить общую память, а какие уже нет — в этом и фокус задачи


👉Запустить код - https://go.dev/play/p/M_JvkzNmVY9

@golangtests
  • 👍 8
  • 🔥 3
  • ❤ 2
  • 👎 1
Post #886 2.68K
👣 Как работает планировщик задач в Golang под капотом

Планировщик задач в Golang работает по схеме G-P-M:
горутины (G) распределяются по логическим процессорам (P), а те в свою очередь выполняются на системных потоках (M).
Так достигается мультиплексирование миллионов горутин на ограниченное число потоков ОС.

Когда горутина блокируется (syscall, mutex, канал без данных) - её паркуют, а поток освобождают.
Когда появляется работа — планировщик перебрасывает G между очередями P и старается держать все ядра загруженными.

Чтобы код работал эффективно:
- не плодите лишние горутины
- избегайте долгих блокировок
- контролируйте GOMAXPROCS
- проверяйте количество G через runtime


package main

import (
"fmt"
"runtime"
"time"
)

func main() {
runtime.GOMAXPROCS(runtime.NumCPU())

for i := 0; i < 5; i++ {
go func(id int) {
for {
fmt.Println("goroutine", id, "работает на потоке")
time.Sleep(200 * time.Millisecond)
}
}(i)
}

for {
fmt.Println("горутин сейчас:", runtime.NumGoroutine())
time.Sleep(time.Second)
}
}


https://www.youtube.com/shorts/XQmGO29JE7w

#junior #golang
  • 👍 7
  • 🔥 1
Post #885 3.05K
🚀 САМЫЙ ЭФФЕКТИВНЫЙ АЛГОРИТМ ПОИСКА В GO

Бинарный поиск - один из самых быстрых способов найти элемент в отсортированном слайсе. Вместо полного прохода он делит диапазон пополам, работая за O(log n). Используй его везде, где данные можно заранее отсортировать - прирост скорости огромный.


package main
import "fmt"

func binSearch(a []int, t int) bool {
l, r := 0, len(a)-1
for l <= r {
m := (l + r) / 2
if a[m] == t { return true }
if a[m] < t { l = m + 1 } else { r = m - 1 }
}
return false
}

func main() {
nums := []int{1,4,7,9,15,20,33,42}
fmt.Println(binSearch(nums, 33))
fmt.Println(binSearch(nums, 100))
}
  • 👍 5
  • ❤ 3
  • 🔥 3
Post #884 12.6K
👣 Вот ещё одна задача на Go, которая проверяет знание интерфейсов, nil и типов - частая ловушка даже для опытных разработчиков


package main

import (
"errors"
"fmt"
)

type myError struct{}

func (m *myError) Error() string {
return "something went wrong"
}

func returnsNilError() error {
var e *myError = nil
return e
}

func main() {
err := returnsNilError()
fmt.Println("err == nil:", err == nil) // неожиданно false
fmt.Println("err:", err)
}


Вывод: err == nil: false
err: something went wrong


⚡️ Заплатить код: https://go.dev/play/p/lrw5DEthEvS

@golangtests
  • 🔥 5
  • 👍 2
  • 🥰 1
Post #882 2.46K
👣 Что выведет код (Go 1.25)?


package main

import "fmt"

func main() {
s := []int{1, 2, 3, 4}
t := s[1:3] // t = [2, 3]
t = append(t, 99) // возможно, меняет s[3]
s[2] = 42 // изменяем s напрямую

fmt.Println("s:", s)
fmt.Println("t:", t)
}


Ответ :

s: [1 2 42 99]
t: [2 42 99]


⚡️ Запустить код: https://go.dev/play/p/96RGE1ea6Sq
  • 👍 9
  • ❤ 5
  • 🔥 4
  • 🤔 2
Post #881 2.62K
  • 👍 3
Post #880 2.7K
  • 🔥 7
  • ❤ 1
  • 🥰 1
Post #878 2.9K
👣 Go: Тонкая ловушка - typed nil в интерфейсе.

Если присвоить интерфейсу значение nil конкретного типа, сравнение с nil вернёт false, потому что у интерфейса уже есть динамический тип. Запомни правило: интерфейс равен nil только когда и тип отсутствует, и значение nil.


package main

import (
"fmt"
"os"
)

func main() {
var err error = (*os.PathError)(nil)
fmt.Println(err == nil)
}


https://www.youtube.com/shorts/vobogCxfoLk
  • 👍 9
  • 🤯 4
  • ❤ 2
Post #877 2.64K
  • 👍 1
Post #875 2.61K
  • ❤ 2
  • 🤔 1
Older posts →
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →