Параллельный Search с каналом ошибок

Код-ревьюMiddle15 минпроверка — Спринт10-t039

Метод Search параллельно запрашивает Storage.Items и Storage.Tags в двух горутинах, отправляет ошибки в канал errs и собирает items с их тегами вложенным циклом. Рабочий ли это код? Перепишите.

Как на собесе: 15 минут, ищи всё — гонки, утечки, ошибки, безопасность, стиль. Ссылайся на строки.

review.gogo
1type Storage interface {2	Items(ids []string) ([]*Item, error)3	Tags(ctx context.Context, ids []string) ([]*Tag, error)4}5 6type Item struct {7	ID      int8	Created uint89}10 11type Tag struct {12	ItemID int13	Low    float6414	High   float6415}16 17type ItemWithTags struct {18	ID   int19	Tags []*Tag20}21 22type GetReq struct {23	IDs []string24}25 26type GetResp struct {27	Items []*ItemWithTags28}29 30type Server struct {31	storage Storage32}33 34func (s Server) Search(ctx context.Context, req GetReq) (*GetResp, error) {35	var errs chan error36 37	var items []*Item38	go func() {39		res, e := s.storage.Items(req.IDs)40		if e != nil {41			errs <- e42			return43		}44 45		items = res46	}()47 48	var tags []*Tag49	go func() {50		res, e := s.storage.Tags(context.Background(), req.IDs)51		if e != nil {52			errs <- e53			return54		}55 56		tags = res57	}()58 59	for e := range errs {60		if e != nil {61			return nil, e62		}63	}64 65	out := []*ItemWithTags{}66 67	for _, item := range items {68		for _, tag := range tags {69			if tag.ItemID == item.ID {70				out = append(out, &ItemWithTags{ID: item.ID, Tags: []*Tag{tag}})71			}72		}73	}74 75	return &GetResp{Items: out}, nil76}
⌘ Enter — проверить