Как написать функцию reverse для стандартной библиотеки Go с учетом общего использования
Что проверяют
Умеете ли вы писать обобщённый код на Go так, как это делает стандартная библиотека: на дженериках, без лишних аллокаций и с понятным контрактом. Следом обычно спрашивают, почему функция меняет срез на месте и как развернуть строку.
Как отвечать
Сначала скажите, что такая функция уже есть: slices.Reverse появилась в Go 1.21. Её реализация и есть правильный ответ:
func Reverse[S ~[]E, E any](s S) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}Дальше объясните решения:
- дженерики, а не
[]interface{}:[]intнельзя передать туда, где ждут[]any, пришлось бы копировать в новый срез и терять типы; S ~[]E: так устроен весь пакетslices. Ограничение принимает и именованные типы вродеtype IDs []int, а там, где функция возвращает срез, она возвращает тот же тип;- разворот на месте: O(n) по времени, O(1) по памяти, ничего не выделяется. Кому нужна копия, сделает
slices.Cloneи развернёт её. Результат не возвращается, чтобы никто не решил, что исходник остался нетронутым; - пустой и
nilсрез: цикл просто не выполнится, отдельная проверка не нужна; - тесты: табличный тест на пустой срез, один элемент, чётную и нечётную длину, плюс фаззинг через
go test -fuzzсо свойством «двойной разворот возвращает исходный срез».
Если нужно только пройти по элементам с конца, разворачивать ничего не надо: с Go 1.23 есть итератор slices.Backward.
Где спотыкаются
Пишут на []interface{}, как до Go 1.18. На собеседовании по современному Go это выглядит устаревшим.
Разворачивают строку через []byte. Многобайтовые символы UTF-8 ломаются, нужно хотя бы []rune, а для эмодзи и комбинируемых символов не хватает и его.
Не думают об общих массивах: подсрез s[2:5] делит память с исходным, и разворот на месте меняет оба. Об этом стоит сказать самому.