TGViewer
Channel Public Channel
Thank Go!

Thank Go!

@thank_go

Здравый взгляд на язык программирования Go.

Злой админ @nalgeon. Добрый админ @mikeberezin. Рекламы нет.
Subscribers
3.32K
Photos
12
Videos
0
Links
140
Recent Posts 20 shown
Post #283 1.09K
Шпаргалка по многозадачности

Подготовил интерактивную памятку по многозадачности в Go:

— Горутины, каналы, селект.
— Конвейеры, время, контекст.
— Группы ожидания, гонки, мьютексы.
— Семафоры, сигналы, атомики.
— Тестирование, внутрянка, диагностика.

Все на англ. Делать на русском лень, простите.

Есть и PDF-версия.

https://antonz.org/go-concurrency-distilled
  • 🔥 39
  • ❤ 4
  • 👍 3
Post #282 2.36K
Множественный разврат возврат

Сколько значений можно вернуть из функции через multi-return? Да сколько угодно!

Например, функция readFloat в стандартной библиотеке возвращает семь штук.

Вот это я понимаю, функциональное программирование 😁
  • 😁 34
  • 👍 1
  • 😢 1
Post #281 2.04K
Ошибка ошибка ошибка ошибка

Это выражение выглядит странновато, но оно полностью корректно:


error.Error(errors.ErrUnsupported)


errors.ErrUnsupported — реальная, хотя и не слишком известная ошибка в пакете errors:


var ErrUnsupported = New("unsupported operation")


error — реальное название интерфейсного типа, а Error — метод на этом интерфейсе:


type error interface {
Error() string
}


Метод любого типа можно вызвать как обычную функцию, так что эти два вызова эквивалентны:


var err = errors.New("oops")
err.Error() // "oops"
error.Error(err) // "oops"


В результате получается так:


s := error.Error(errors.ErrUnsupported)
println(s)
// unsupported operation


🤓
  • ❤ 18
  • 🔥 7
  • 👍 5
  • 😁 3
Post #279 2.74K
JSON v2 в Go 1.27

Вторая версия json-пакета впервые появилась как экспериментальная в Go 1.25. Два релиза ее обкатывали, и наконец окончательно выпустили в 1.27.

В связи с этим актуализировал свой гайд по отличиям между json и json/v2 (их много!).

Все разобрано на конкретных примерах:

https://habr.com/ru/articles/923404/
  • 👍 28
Post #278 3.21K
strings.CutLast

Из небольших изменений в Go 1.27 я больше всего рад strings.CutLast. Эта функция делит строку по последнему вхождению разделителя.

Конечно, реализовать ее несложно и самому:


// CutLast делит s на части до и после последнего вхождения sep.
func CutLast(s, sep string) (before, after string, found bool) {
if i := strings.LastIndex(s, sep); i >= 0 {
return s[:i], s[i+len(sep):], true
}
return s, "", false
}


Но приятно, что теперь делать этого не придется:


s := "go|for|it"
_, last, ok := strings.CutLast(s, "|")
fmt.Println(last, ok)
// it true


Интересно, что Cut добавили еще в 1.20, а CutLast почему-то пробыла в немилости аж 7 релизов :)
  • 🎉 19
  • ❤ 7
Post #277 2.62K
Generic-методы и их последствия

Думаю, «джавафикация» экосистемы Go только усилится с появлением в 1.27 дженерик-методов (это методы, у которых может быть собственный параметр типа).


// Sum - сумма целых чисел.
type Sum[T int | int64] struct {
v T
}

// Add добавляет число к сумме, приводя его к целому, если нужно.
func (s *Sum[T]) Add[V int | int64 | float64](v V) *Sum[T] {
s.v += T(v)
return s
}


В этом примере T — параметр типа на структуре, а V — параметр типа на методе. В предыдущих версиях Go параметры вроде V приводили к ошибке "method must have no type parameters", а теперь будут разрешены.


var s Sum[int]
s.Add(30).Add(10).Add(2.3)
fmt.Println(s.Val())
// 42


Вряд ли мы увидим ненужные дженерик-абстракции прямо в стдлибе, но многие авторы сторонних пакетов и приложений с радостью начнут (точнее, продолжат с удвоенной силой) их городить.
  • 🔥 15
  • 😱 9
  • 👍 3
  • 😢 1
Post #276 2.91K
Тур по Go 1.27

Со дня на день выйдет Go 1.27, так что самое время почитать, что там внутри.

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

Краткое содержание для ленивых:

— Generic-методы с собственными (не привязанными к типу) параметрами
— Компактные литералы для embedded-структур
— Диагностика утечек горутин в продакшене (было experiment в 1.26)
— UUID v4 и v7
— JSON v2 в продакшене (был experiment в 1.26)
— Универсальный SIMD (experiment)
— strings.CutLast (давно о ней мечтал)
— Интерфейс maphash.Hasher
— synctest.Sleep (= time.Sleep + synctest.Wait)

https://victoriametrics.com/blog/go-1-27
  • ❤ 18
Post #275 3.03K
Делаем себе сложно

Хотел я вам написать, как можно начать с очень простой реализации Backward, и прийти к чему-то похожему на стдлибу.

Но заметка получилась великовата для канала, поэтому вынес ее на хабр. Не уверен, что тамошние обитатели будут в восторге.

Вам же заметку рекомендую — в ней есть jira, жабы, и нелепый авторский юмор.

https://habr.com/ru/articles/1065634
  • 👍 18
  • ❤ 7
Post #274 2.86K
  • 😱 4
  • 👍 1
  • 😁 1
Post #273 3.04K
Что внутри у интерфейса

Многие знают, что интерфейс в Go реализован как обертка, внутри которой два указателя — на конкретное значение и на его тип.

Но строго говоря, все несколько сложнее.

Вот интерфейс:

type iface struct {
tab *abi.ITab
data unsafe.Pointer
}


data — это действительно указатель. Если мы заворачиваем в интерфейс значение, в data будет указатель на него (а само значение переедет в кучу при необходимости).

Если же заворачиваем указатель (например *os.File в io.Reader), то в data будет ровно этот самый указатель.

А вот tab — не просто конкретный тип, а целая пачка метаданных:

type ITab struct {
Inter *InterfaceType
Type *Type
Hash uint32
Fun [1]uintptr
}


Inter — это тип интерфейса (например, io.Reader), Type — конкретный тип (например, *os.File).

Hash — это хеш от конкретного типа. Он используется для быстрого выбора подходящей ветки в переключателях типа (type switch). О нем еще поговорим отдельно.

А Fun — это массив указателей на методы типа, которые реализуют интерфейс (например, (*os.File).Read).

Из объявления Fun [1]uintptr может показаться, что у интерфейса может быть только один метод (что, как мы знаем, не так). Но на самом деле это фикция. Указателей будет ровно столько, сколько методов у интерфейса, просто Fun[0] — первый из них.

Ах да, пустой интерфейс (any) реализован иначе. О нем тоже отдельно поговорим.

Уфф.
  • 🔥 34
  • 👍 1
Post #272 2.47K
Метод-выражение

Все, конечно, знают, что такое метод структуры:

type counter struct {
val uint
}

func (c *counter) inc() {
c.val++
}


Но не все знают, что метод реализован как обычная функция, у которой первым параметром идет значение получателя (или указатель на него):

// реализованы одинаково
func (c *counter) inc()
func inc(c *counter)


На практике это означает, что вы можете использовать метод как функцию:

inc := (*counter).inc
c := new(counter)
inc(c)
inc(c)
inc(c)
println(c.val)
// 3


Называется такое чудо «метод-выражение» (method expression).

P.S. А бывает еще метод-значение — это более причудливый зверь, замыкание на получателе.
  • 😱 12
  • ❤ 7
  • 👍 6
  • 😁 2
Post #271 2.92K
Рубрика «ненормальное программирование»

Запихиваем максимальное количество встроенных функций в одно выражение:

println(max(
len(append(make([]int, 1, 2), *new(int))),
cap(make(chan struct{}, 3)),
copy(make([]byte, 5), "hello"),
min(int(real(complex(1, 2))), int(imag(complex(3, 4)))),
map[any]int{}[recover()],
))


Здесь их 13 🤓 В порядке появления:

println
max
len
append
make
new
cap
copy
min
real
complex
imag
recover


Для большего погружения, попробуйте завтра писать код в таком стиле, и обязательно отправьте его на ревью.
  • 😁 27
  • ❤ 4
  • 🎉 3
  • 👍 1
  • 😢 1
Post #270 2.69K
18 встроенных функций Go

Вам, конечно же, не терпится увидеть полный список встроенных функций (нет). Ну раз вы так настаиваете, то вот он:

make создает новый срез, карту или канал.
new аллоцирует память под переменную.

append добавляет одно или несколько значений в срез.
copy копирует элементы из одного среза в другой (или байты из строки в срез).
delete удаляет элемент из карты.
clear очищает срез или карту.

len и cap возвращают длину/емкость контейнера (строка, срез, карта, канал).

close закрывает канал.

min и max возвращают минимальный/максимальный из аргументов.

complex, real и imag собирают и разбирают комплексные числа.

panic выбрасывает панику, а recover ее ловит.

print и println печатают аргументы.

Можете ради интереса посчитать, сколько из них вы реально используете :)
  • 👍 21
  • ❤ 7
  • 🔥 2
  • 😁 1
Post #269 2.57K
Встроенные функции

Все регулярно используют встроенные функции (built-in). Это функции, которые не требуют импорта пакета, вроде len, make или panic.

Но что такое вообще встроенная функция? Например, len в документации заявлена как часть пакета builtin:

func len(v Type) int


Во-первых, что это еще за builtin-пакет и тип Type в нем?

Во-вторых, len явно дженерик, так где же наша любимая абракадабра вида len[S ~[]E, E any](x S), ну или хотя бы len[T any](s []T)?

И как вообще это добро работало начиная с версии Go 1.0, когда никаких дженериков даже на горизонте не было?

Суровая правда в том, что len — вообще не функция. У нее нет собственного фрейма на стеке, как у обычных функций, и других присущих им особенностей.

На самом деле, компилятор превращает вызов len(s) в прямое чтение s.len, где len — это обычное поле структуры среза (или аналогичное поле строки, карты или канала):

type slice struct {
array unsafe.Pointer
len int
cap int
}


Не все билтины настолько просты. Например, make и panic компилируются в реальные вызовы функций (runtime.makeslice, runtime.gopanic) — с фреймом и всем полагающимся.

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

Что касается Type, то это просто заглушка. Пакет builtin не компилируется и существует только ради документации.

Такое вот коварство.
  • 🔥 32
  • 👍 8
  • ❤ 4
Post #268 2.65K
Функциональные опции

Должен признаться, что терпеть не могу паттерн «функциональных опций», полюбившийся многим программистам на Go.

И особенно я не рад, что он пробрался в стдлибу вместе с новым пакетом json/v2 (финализирован в Go 1.27):

type Options interface { ... }

func Deterministic(v bool) Options
func FormatNilMapAsNull(v bool) Options
func FormatNilSliceAsNull(v bool) Options
// ...

func Marshal(in any, opts ...Options) (out []byte, err error)


Обычная структура (см. скриншот) лучше во всех отношениях, на мой взгляд.

А вы что думаете?
  • 👍 9
  • 🔥 1
Post #267 3.16K
string ⇄ []byte

Все знают, как преобразовать байтовый срез в строку и обратно:

b := []byte{...}
s := string(b) // 1


s := "..."
b := []byte(s) // 2


И ➀ string(b) и ➁ []byte(s) создают копию данных длиной len(x) — на стеке или в куче, в зависимости от ситуации.

Цикл с преобразованием string(b) тоже создает копию:

for _, r := range string(b) {
...
}


А вот цикл с преобразованием []byte(s) копию никогда не создает — итерация идет прямо по байтам оригинальной строки:

for _, ch := range []byte(s) {
...
}


Такая оптимизация компилятора.
  • ❤ 28
  • 👍 24
Post #266 3.45K
Флаг -race

Флаг -race включает проверку на гонки в коде (потенциальный конфликт при доступе к общим данным из нескольких горутин). Если при выполнении рантайм видит гонку, он пишет, где именно та произошла.

Несложно догадаться, что проверка эта небесплатная. С включенным детектором гонок приложение использует в 5–10 раз больше памяти, а работает в 2–20 раз медленнее.

Надо ли собирать продакшен с включенным -race? Разве что если в ваши планы входит потом «оптимизировать» производительность в 10 раз и получить за это почет и уважение. Ну или по башке, если кто-то проверит, что именно вы там наоптимизировали 😅
  • 😁 33
  • 👍 7
  • ❤ 1
Post #265 3.07K
  • 😁 15
  • 🔥 4
  • 😱 3
Post #264 3.07K
Грязный make

А хотите по-настоящему злую штуку? В стандартной библиотеке есть функция, которая создает срез байт, не зануляя память:

// MakeNoZero makes a slice of length n
// and capacity of at least n bytes
// without zeroing the bytes.
// It is the caller's responsibility
// to ensure uninitialized bytes
// do not leak to the end user.
func MakeNoZero(n int) []byte


Функция находится в пакете internal/bytealg, так что напрямую ее не вызвать. Но через go:linkname — можно:

//go:linkname makeNoZero internal/bytealg.MakeNoZero
func makeNoZero(n int) []byte

func main() {
data := makeNoZero(10)
// ...
}


Никогда так не делайте 😈
  • 😱 20
  • ❤ 6
  • 🔥 5
Older posts →

About this channel

How can I read @thank_go without a Telegram account?
TGViewer shows the public web preview Telegram publishes for Thank Go!: recent posts, photos, videos and the subscriber count, with no app, login or account.
How many subscribers does Thank Go! have?
Thank Go! (@thank_go) has 3.32K subscribers on Telegram, refreshed roughly every 30 minutes.
Does Thank Go! know I viewed it here?
No. Public channel previews carry no viewer identity, and TGViewer has no accounts or tracking of what you look up.
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 →