Pull to refresh

Comments 7

Вот с указателями тоже заметил.

Такая функция:
func ToRecords(records <-chan Record) <-chan Records


по тестам производительности работает ощутимо быстрее такой:
func ToRecords(records <-chan *Record) <-chan *Records


Сделали по «быстрому» варианту, но терзают смутные сомнения…
ИМХО, все очень сильно зависит от того, что вы делаете. В большинстве случаев я бы использовал указатели, потому что они работают понятнее и предсказуемее, чем значения, для всех, кто работал с другими ООП-языками раньше. Плюс, если вы зовете методы, которые принимают интерфейсы и передаете туда значения, то всё равно (на куче?) создается объект с указателем на него, и потом удаляется, и это делается каждый раз при вызове.

Ещё вариант, когда использование передачи по значению, ИМХО, оправдано — это когда вы точно уверены, что эта структура должна быть 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
Одним из возможных решений для прерывания 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().
Sign up to leave a comment.

Articles