> Как сделать так, чтобы изменения слайса в функции были видны в вызывающей функции (Go)
Уровень: middle · Роль: backend · Категория: Технические вопросы
Компании: Ozon, Black Wall Group (BWG)
Стек: Go
> Пример ответа
Короткий ответ
Слайс в Go - это структура из трёх полей: указатель на массив, длина и ёмкость. При передаче в функцию копируется заголовок слайса, но не сам массив. Чтобы изменения были видны в вызывающей функции, нужно либо передавать указатель на слайс (*[]T), либо возвращать изменённый слайс из функции и присваивать его в вызывающей стороне. Если меняются только элементы внутри существующего массива - изменения видны без дополнительных действий.
Подробное объяснение
Слайс - это ссылочный тип, но передаётся по значению. Копируется заголовок: указатель, len, cap. Поэтому:
- Изменение элементов
s[i] = x- видно, так как указатель ведёт на тот же массив. - Добавление через
appendможет пересоздать массив, если ёмкости не хватает - тогда исходный слайс не изменится. - Изменение длины или ёмкости внутри функции не влияет на внешний слайс.
Чтобы гарантированно применить изменения структуры слайса (добавление, удаление, перестановка), используйте:
- Указатель на слайс:
func f(s *[]int)- внутри работаете с*s. - Возврат нового слайса:
func f(s []int) []int- вызывающий присваивает результат.
Второй вариант более идиоматичен для Go, так как избегает мутаций и упрощает чтение кода.
На практике
- Если функция только читает или меняет элементы - передавайте слайс по значению.
- Если функция добавляет или удаляет элементы - возвращайте новый слайс.
- Указатель на слайс оправдан, когда нужно менять сам слайс в нескольких местах или в цикле, но это менее читаемо.
- Помните про
append: он возвращает новый слайс, поэтому без присваивания результат теряется.
Пример кода
GO// Изменение элементов - работает без возвратаfunc setFirst(s []int, v int) {s[0] = v}// Добавление - нужно вернуть новый слайсfunc appendValue(s []int, v int) []int {return append(s, v)}// Через указатель - тоже работает, но менее идиоматичноfunc appendValuePtr(s *[]int, v int) {*s = append(*s, v)}func main() {a := []int{1, 2, 3}setFirst(a, 10) // a = [10, 2, 3]a = appendValue(a, 4) // a = [10, 2, 3, 4]b := []int{1, 2}appendValuePtr(&b, 5) // b = [1, 2, 5]}
Как отвечать на собеседовании
Начните с того, что слайс - это структура с указателем на массив. Объясните разницу между изменением элементов и изменением структуры слайса. Приведите оба способа: указатель и возврат значения. Подчеркните, что возврат - более идиоматичный подход. Если спросят про производительность - упомяните, что копирование заголовка слайса дешёвое (3 слова), а указатель на слайс добавляет лишний уровень косвенности.
Что проверяет интервьюер
- Понимание внутреннего устройства слайса (header, указатель, len, cap).
- Знание поведения
appendи случаев переаллокации массива. - Умение выбирать между передачей по значению и по указателю.
- Понимание идиоматичности Go-кода.
Типичные ошибки
- Утверждение, что слайс передаётся по ссылке - это неверно, передаётся копия заголовка.
- Забывают, что
appendможет вернуть новый слайс, и не присваивают результат. - Передают указатель на слайс там, где достаточно возврата значения - это усложняет код.
- Путают изменение элемента (
s[i] = x) с изменением самого слайса (s = append(s, x)).
> Похожие задачи по backend
Какое взаимодействие между сервисами предпочтительнее: синхронное или асинхронное
Как защититься от SQL-инъекций
Являются ли мапы в Go потокобезопасными
Какие способы копирования слайса в Go существуют
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью