Comments 7
Вот с указателями тоже заметил.
Такая функция:
по тестам производительности работает ощутимо быстрее такой:
Сделали по «быстрому» варианту, но терзают смутные сомнения…
Такая функция:
func ToRecords(records <-chan Record) <-chan Records
по тестам производительности работает ощутимо быстрее такой:
func ToRecords(records <-chan *Record) <-chan *Records
Сделали по «быстрому» варианту, но терзают смутные сомнения…
ИМХО, все очень сильно зависит от того, что вы делаете. В большинстве случаев я бы использовал указатели, потому что они работают понятнее и предсказуемее, чем значения, для всех, кто работал с другими ООП-языками раньше. Плюс, если вы зовете методы, которые принимают интерфейсы и передаете туда значения, то всё равно (на куче?) создается объект с указателем на него, и потом удаляется, и это делается каждый раз при вызове.
Ещё вариант, когда использование передачи по значению, ИМХО, оправдано — это когда вы точно уверены, что эта структура должна быть immutable и при этом она не используется в слайсах, например (иначе при росте слайса копируются не указатели, а сами значения структур, и это может быть очень очень медленно).
Ещё вариант, когда использование передачи по значению, ИМХО, оправдано — это когда вы точно уверены, что эта структура должна быть immutable и при этом она не используется в слайсах, например (иначе при росте слайса копируются не указатели, а сами значения структур, и это может быть очень очень медленно).
Так chan же сам по себе ссылка на структуру и при передаче его параметром там и так указатель копируется. Сделать указатель на указатель — просто вызвать двойное преобразование адреса при любом обращении к параметру. Потому и медленнее.
А если затестить вот так
То результаты такие
а то оригинальный бенчмарк не корректен, сравниваем разные вещи
func byValueP(in *foo) bar {
//...
}
То результаты такие
BenchmarkByPointer-4 20000000 107 ns/op
BenchmarkByValue-4 50000000 41.9 ns/op
BenchmarkByValueP-4 50000000 32.6 ns/op
а то оригинальный бенчмарк не корректен, сравниваем разные вещи
Иногда нужно поискать среди обернутых ошибок конкретную.
errors.Cause (из pkg/errors) позволяет сравнить только с первоначальной. Поэтому решил остановиться на xerrors с его Is, As
errors.Cause (из pkg/errors) позволяет сравнить только с первоначальной. Поэтому решил остановиться на xerrors с его Is, As
Одним из возможных решений для прерывания for/switch или for/select является использование метки:
Я бы еще один метод описАл — сделать так, что бы for был в конце тела функции (т.е. что бы после for ничего кроме описанного в defer не надо было делать). Тогда выходить из цикла нет нужды — можно просто сделать return.
По сути это довольно часто встречающийся паттерн, особенно для gorutine обрабатывающих события из каналов. Там почти всегда по такой схеме:
func someFunc(...) ... {
<инициализация всего что нужно + defer-ы>
for {
select {
case <-ch:
// Do something
case <-ctx.Done():
return ...
}
}
}
Лично у меня метки вызывают ощущения костылей… видимо сказывается изучение в институте pascal :)
Отличная статься, спасибо!
Нет ли нарушения инкапсуляции insert() при проверке ошибки на *db.DBError? Что если сделать ретрай в insert()? Там где явно идёт обращение к бд.
Или внутри insert() обернуть ошибку во что то, что покажет пользователю insert() возможность ретрая, например RetryableError тип, или тип ошибки с дополнительным методом .IsRetryable().
Нет ли нарушения инкапсуляции insert() при проверке ошибки на *db.DBError? Что если сделать ретрай в insert()? Там где явно идёт обращение к бд.
Или внутри insert() обернуть ошибку во что то, что покажет пользователю insert() возможность ретрая, например RetryableError тип, или тип ошибки с дополнительным методом .IsRetryable().
Sign up to leave a comment.
Топ-10 самых распространенных ошибок, которые мне встречались в Go-проектах