Ответ
Хотя и Array, и String соответствуют протоколу Sequence, их внутреннее устройство и семантика итерации различаются.
Перебор массива (Array)
Массив — это коллекция элементов с целочисленной индексацией (O(1) доступ).
let numbers: [Int] = [10, 20, 30]
// 1. Цикл for-in
for number in numbers {
print(number) // 10, 20, 30
}
// 2. Прямой доступ по индексу
for i in 0..<numbers.count {
print(numbers[i]) // 10, 20, 30
}
Перебор строки (String)
Строка — это коллекция расширенных графемных кластеров (Character). Доступ по индексу не является O(1) из-за Unicode.
let greeting = "Hello🇺🇦"
// 1. Цикл for-in (перебор Character)
for character in greeting {
print(character) // H, e, l, l, o, 🇺🇦 (флаг как один Character)
}
// 2. Использование индексов String.Index
for index in greeting.indices {
print(greeting[index]) // Аналогичный вывод
}
// НЕВЕРНО: print(greeting[0]) - Ошибка компиляции
Сравнительная таблица
| Аспект | Массив (Array<T>) |
Строка (String) |
|---|---|---|
| Тип элемента | Любой тип T |
Character (графемный кластер) |
| Индексация | Int (быстрая, O(1)) |
String.Index (относительная позиция) |
| Изменяемость | Элементы можно менять по индексу | Строка — value type, изменение создает копию |
| Сложность итерации | O(n) | O(n), но с большей константой из-за Unicode |
Почему так? Строка должна корректно обрабатывать составные символы (например, "é" = e + ́ ) и эмодзи (например, "👨👩👧👦"), поэтому прямой доступ по целочисленному индексу невозможен.