> Почему при взятии подмассива массива возвращается ArraySlice, а при взятии подстроки из String возвращается Substring (iOS, Swift)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: Яндекс

Стек: iOS, Swift

> Пример ответа

Короткий ответ

ArraySlice и Substring - это легковесные view поверх исходной коллекции, которые разделяют память с оригиналом. Они возвращаются вместо новых копий, чтобы избежать лишних аллокаций и копирований при частых операциях нарезки. Разные имена подчёркивают, что это разные типы с разными контрактами: Substring не является String, а ArraySlice не является Array, хотя оба сохраняют большинство API родительского типа.

Подробное объяснение

В Swift строки и массивы - это value types с семантикой копирования при записи (copy-on-write). Если бы prefix, suffix или dropFirst возвращали новый Array или String, каждая операция требовала бы полного копирования элементов. Для больших коллекций это дорого.

Вместо этого Swift возвращает ArraySlice и Substring - структуры, которые хранят ссылку на исходный буфер памяти и диапазон индексов. Создание такого view - O(1) по времени и памяти.

Почему не один общий тип? Потому что:

  • Substring должен поддерживать StringProtocol, чтобы можно было передавать его в функции, ожидающие String (через generic constraint).
  • ArraySlice должен сохранять индексную семантику Array (Int-based), но с возможным ненулевым начальным индексом.
  • Разделение типов позволяет компилятору точнее оптимизировать и предотвращает случайное смешение разных коллекций.

Важно: Substring и ArraySlice живут только пока жив оригинал. Если оригинал освобождается, view всё равно удерживает буфер, поэтому длительное хранение среза может удерживать всю память оригинала.

На практике

  • Не храните Substring или ArraySlice долго - если нужна постоянная строка/массив, конвертируйте явно: String(substring), Array(slice).
  • Индексы ArraySlice не обязательно начинаются с 0: array[2...4] даст slice с startIndex == 2.
  • Для Substring индексы совпадают с индексами исходной String, но тип индекса - String.Index, а не Int.
  • Операции над slice (например, map, filter) возвращают уже обычный Array, потому что результат не является view.
  • При передаче slice в функцию, ожидающую Array, нужна явная конвертация - это защита от случайного удержания памяти.

Пример кода

SWIFT
let array = [1, 2, 3, 4, 5]
let slice = array[1...3]
print(type(of: slice)) // ArraySlice<Int>
print(slice.startIndex) // 1
print(Array(slice)) // [2, 3, 4]
let str = "Hello, world"
let sub = str.prefix(5)
print(type(of: sub)) // Substring
print(String(sub)) // "Hello"
// Долгое хранение slice удерживает весь массив
var bigArray = Array(0..<100_000)
let smallSlice = bigArray[0..<10]
bigArray = [] // память всё ещё занята, пока жив smallSlice

Как отвечать на собеседовании

Начните с ключевой идеи: это оптимизация производительности через copy-on-write и view поверх буфера. Затем объясните разницу контрактов: Substring - это StringProtocol, ArraySlice - RandomAccessCollection. Упомяните, что оба типа - value types, но разделяют память с оригиналом. Подчеркните практическое следствие: не хранить срезы долго, конвертировать при необходимости. Если спросят про индексы - упомяните ненулевой startIndex у ArraySlice.

Что проверяет интервьюер

  • Понимание модели памяти Swift: value types, copy-on-write, буферы.
  • Знание стандартной библиотеки: ArraySlice, Substring, StringProtocol.
  • Умение объяснять trade-off между производительностью и удобством API.
  • Осознание опасности удержания памяти через срезы.
  • Понимание, почему дизайн разделяет типы, а не использует один универсальный.

Типичные ошибки

  • Утверждение, что Substring - это подкласс String (это не так, это отдельная структура).
  • Забывают, что ArraySlice может иметь ненулевой startIndex.
  • Говорят, что срез копирует данные - это неверно, копирование происходит только при изменении (copy-on-write).
  • Не упоминают, что срез удерживает весь оригинальный буфер, а не только свои элементы.
  • Путают Substring с String.Index - это разные вещи: Substring - коллекция, String.Index - позиция.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью