1. Что такое Array и какие у него правила
Если вы только начинаете, кажется, что переменных достаточно: ну сделаю book1, book2, book3 — и готов «список книг». Но это быстро превращается в головную боль: как добавить четвёртую книгу, как перебрать все, как найти «самую короткую строку»? Массив решает эту проблему: он хранит много значений одного типа, в порядке добавления, и позволяет писать универсальный код.
Представьте, что вы ведёте маленькую консольную программу «Список чтения». Вчера вы могли хранить одну книгу в String. Сегодня вы хотите хранить список названий книг. Вот тут Array и выходит на сцену — как полка, на которую можно поставить не одну книгу, а много.
Когда вы слышите слово «массив», важно не утонуть в терминах и держать в голове две простые мысли.
- Первая: массив хранит элементы в порядке.
- Вторая: все элементы массива имеют один и тот же тип. Никаких «тут Int, тут String, а тут ещё котик» — котик будет позже, когда вы изучите другие инструменты (и, надеюсь, не в продакшене).
То есть массив можно описать так: «упорядоченная коллекция значений одного типа». Порядок важен, потому что «первый», «второй», «третий» — это реальная характеристика массива. И позже мы будем получать элементы по позиции (по индексу), перебирать их циклом и изменять.
Индекс в массиве
Мало знать, что у вас есть массив, нужно еще и уметь работать с его элементами. Чтобы получить элемент из массива, нужно знать его позицию. Эта позиция называется индексом.
Индексы в Swift (как и во многих языках программирования) начинаются с нуля, а не с единицы. Это может сначала выглядеть непривычно, но быстро становится естественным.
Представьте массив книг:
let books = ["Dune", "Foundation", "The Hobbit"] // три стоки упакованные в виде массива
Если пронумеровать элементы, получится так:
| Индекс | Элемент |
|---|---|
|
|
|
|
|
|
То есть:
- первый элемент имеет индекс 0
- второй — 1
- третий — 2
Количество элементов можно узнать через count. А последний индекс всегда равен count - 1.
print(books.count) // 3
Схема индексов
flowchart LR
A["0 Dune"] --> B["1 Foundation"]
B --> C["2 The Hobbit"]
Главное правило: индекс всегда должен находиться в диапазоне от 0 до count - 1. Если попытаться обратиться к несуществующему индексу, программа завершится с ошибкой.
Например, у массива из 3 элементов индекс 3 уже не существует.
var books = ["Dune", "Foundation", "The Hobbit"]
books[1] = "The Hitchhiker's Guide to the Galaxy"
print(books[0]) // Dune
print(books[1]) // The Hitchhiker's Guide to the Galaxy
print(books[2]) // The Hobbit
Именно поэтому порядок в массиве так важен: каждый элемент имеет свою позицию, и по этой позиции его можно получить или изменить.
2. Создаём массивы: основные способы
Литерал массива
Самый дружелюбный способ создать массив — написать его прямо в коде в квадратных скобках. Это называется литерал массива.
let scores = [10, 20, 30]
print(scores) // [10, 20, 30]
Здесь scores — массив целых чисел. Обратите внимание: мы не писали тип явно. Swift увидел элементы 10, 20, 30 и сделал вывод: «ага, это Int, значит массив [Int]».
Пример со строками:
let authors = ["Le Guin", "Tolkien", "Asimov"]
print(authors) // ["Le Guin", "Tolkien", "Asimov"]
И снова тип выводится автоматически: элементы — строки, значит массив [String].
Вывод типов для массивов
Вывод типов для массива работает прямолинейно: Swift смотрит на элементы в квадратных скобках и пытается понять, какого они типа. Если все элементы одного типа — отлично, тип массива определён.
Сложность начинается, когда элементы выглядят «похожими», но на самом деле имеют разные типы. Например, 1 — это Int, а 1.5 — это уже Double. В одном массиве их вместе держать нельзя, потому что массив должен иметь один тип элементов.
Решение: сделать массив Double, если привести целые к Double.
let measurements: [Double] = [1.0, 1.5, Double(2)]
print(measurements) // [1.0, 1.5, 2.0]
Здесь мы явно подсказали тип массива ([Double]), и Swift согласился. Если бы мы написали [1, 1.5, 2], компилятор бы остановил нас и сказал: «определись, что ты хочешь хранить». И это хорошо: компилятор не даёт сделать программу «почти работающей», но с сюрпризом в рантайме.
Небольшая схема того, как обычно работает вывод типа:
flowchart TD
A["Пишем литерал массива: [ ... ]"] --> B["Swift смотрит на элементы"]
B --> C{"Все элементы одного типа?"}
C -->|Да| D["Тип массива выводится автоматически"]
C -->|Нет| E["Нужна подсказка: явный тип или преобразование"]
Пустой массив и почему [] недостаточно
Самый частый «новичковый» сюрприз: вы хотите начать со списка, в котором пока ничего нет. Логика простая: пишем []. Но Swift не телепат (по крайней мере, официально), и ему нужно понимать, какие элементы будут в массиве.
Так делать нельзя (не компилируется):
- let items = [] — компилятор не знает, что это за элементы.
Правильный способ — дать тип явно. Есть несколько равнозначных вариантов.
Вариант 1: указать тип слева:
let books: [String] = []
print(books) // []
Вариант 2: использовать «пустой конструктор» типа массива:
let ids = [Int]()
print(ids) // []
Вариант 3: через форму Array<T>():
let flags = Array<Bool>()
print(flags) // []
Все три варианта создают пустой массив. Отличается только то, как именно вы объясняете Swift, что будет храниться внутри.
Две записи типа массива: [Int] и Array<Int>
В Swift есть две равноправные формы записи типа массива. Короткая и самая популярная — [Int], [String], [Bool]. Длинная и более «общая» — Array<Int>, Array<String>. Это один и тот же тип, просто разная запись.
Полезно понимать обе формы, потому что в документации и в сигнатурах вы встретите Array<Element> довольно часто. Вообще Array — это обобщённый (generic) тип, и запись Array<String> как раз показывает: «внутри лежит String».
Примеры, которые компилируются одинаково:
let a: [Int] = [1, 2, 3]
let b: Array<Int> = [1, 2, 3]
print(a) // [1, 2, 3]
print(b) // [1, 2, 3]
Иногда длинная форма помогает «не путаться глазами», когда тип становится сложным. Но на базовом уровне чаще всего достаточно короткой формы [T].
let vs var при создании массива
С массивами работает то же правило, что и с любыми значениями в Swift.
Если вы объявили массив через let, вы получаете константу: сам массив нельзя менять (его состав и порядок). Если через var, массив можно изменять.
Сегодня мы ещё не используем append, insert и remove (это будет чуть позже), поэтому разницу почувствуем через простую вещь — переприсваивание.
var tags = ["swift", "basics"]
print(tags) // ["swift", "basics"]
tags = ["swift", "basics", "arrays"]
print(tags) // ["swift", "basics", "arrays"]
А теперь константа:
let roles = ["admin", "editor"]
print(roles) // ["admin", "editor"]
roles можно печатать, передавать в функции, читать, но нельзя менять.
Интуитивная аналогия: let — это «список, который вы распечатали и заламинировали». Читать можно, дописать маркером уже нельзя. var — «список в заметках», который можно редактировать.
Мини-проект: заготовка ReadingList
Чтобы примеры не были набором случайных чисел, начнём мини-проект этого дня — консольную заготовку «ReadingList». Пока она максимально простая: хранит список книг как массив строк и печатает его на экран.
import Foundation
let readingList = [
"The Left Hand of Darkness",
"The Hobbit",
"Foundation"
]
print("Мой список чтения:") // Мой список чтения:
print(readingList) // ["The Left Hand of Darkness", "The Hobbit", "Foundation"]
Здесь import Foundation не обязателен для массивов, но в консольных проектах его часто оставляют «по привычке»: дальше обычно идут строки, ввод, форматирование и прочие удобства. Если хотите, можете убрать — пример всё равно скомпилируется.
Массивы разных типов: [Int], [String], [Bool]
Когда вы только начинаете, может казаться, что тип элементов — это «формальность». Но для Swift это фундамент. Тип определяет, какие операции доступны и что вообще считается корректным.
Например, массив чисел можно использовать для статистики, а массив строк — для вывода сообщений и обработки текста. Swift не позволит вам случайно сложить строку с числом «внутри массива» без явного преобразования — и это экономит часы жизни (обычно те, которые вы бы потратили на поиск глупого бага в 2 ночи).
let attempts: [Int] = [1, 2, 3, 4]
let names: [String] = ["Ana", "Ben"]
let isCompleted: [Bool] = [true, false, false]
print(attempts) // [1, 2, 3, 4]
print(names) // ["Ana", "Ben"]
print(isCompleted) // [true, false, false]
Массив массивов
Частый вопрос: «Можно ли сделать массив, где элементы — тоже массивы?» Технически да: тип будет выглядеть как [[Int]] или [[String]].
Пока без индексов и без «матриц» — просто одна строчка, чтобы идея не выглядела магией:
let shelves: [[String]] = [["A", "B"], ["C"]]
print(shelves) // [["A", "B"], ["C"]]
Запомните сейчас главное: если элемент массива — это другой массив, тип становится «двойными скобками».
Печать массива через print() как инструмент отладки
На старте программирования важно иметь «фонарик», которым вы подсвечиваете то, что реально происходит в программе. print() — это ваш фонарик. И с массивами он работает удобно: Swift печатает массив в читаемом виде, с квадратными скобками и элементами через запятую.
Посмотрим, как это помогает видеть порядок:
let queue = ["first", "second", "third"]
print(queue) // ["first", "second", "third"]
Даже без индексов уже видно: порядок сохранён. Это пригодится позже, когда вы начнёте добавлять и удалять элементы, и print(queue) быстро покажет, «куда уехало» содержимое.
4. Ещё несколько полезных нюансов
Иногда массив создаётся не только литералом. Но пока держимся простых и читаемых вариантов, без забегания в новые темы.
Создать пустой массив и тут же распечатать:
var notes: [String] = []
print(notes) // []
Создать массив из одного элемента (да, это нормально):
let single = ["only one"]
print(single) // ["only one"]
Создать массив и дать ему «говорящее» имя — это не синтаксис, но важная привычка:
let errorMessages = ["Invalid input", "Try again"]
print(errorMessages) // ["Invalid input", "Try again"]
Таблица-шпаргалка по созданию массивов
Иногда полезно увидеть всё в одном месте, особенно если вы только привыкаете к синтаксису. Ниже маленькая шпаргалка именно по созданию, без индексов и мутаций.
| Что хотим сделать | Как пишем | Комментарий |
|---|---|---|
| Создать массив чисел | |
Тип [Int] выведется автоматически |
| Создать массив строк | |
Тип [String] выведется автоматически |
| Создать пустой массив строк | |
Без типа [] непонятен |
| Создать пустой массив Int | |
Удобно, если будете добавлять позже |
| Указать тип через Array<T> | |
Эквивалент [Int], просто другая запись |
5. Типичные ошибки при создании массивов
Ошибка №1: писать let a = [] и ждать, что Swift догадается о типе.
[] — это «ничего», и из этого невозможно вывести, что вы планировали хранить: строки, числа или, возможно, названия котиков. Исправление простое: либо укажите тип слева (let a: [Int] = []), либо создайте через конструктор (let a = [Int]()).
Ошибка №2: пытаться смешивать типы элементов в одном массиве.
Очень частый случай — «хочу [1, 2, 2.5]». Для человека это «числа», а для Swift — Int и Double, то есть разные типы. Правильный путь: решить, какой тип нужен (часто Double) и явно привести элементы: [1.0, 2.0, 2.5] или Double(1).
Ошибка №3: путать массив и кортеж из-за скобок.
[1, 2, 3] — массив, (1, 2, 3) — кортеж. У массива элементы одного типа и их можно хранить сколько угодно. У кортежа фиксированное число элементов, и типы могут быть разными. Если вы случайно написали круглые скобки, код либо не скомпилируется там, где вы ждёте массив, либо поведёт себя «не так».
Ошибка №4: объявить массив через let, а потом ожидать, что он будет расширяться.
Даже если вам «логически» хочется сказать: «само имя списка не меняется», но элементы будут добавляться, — это всё равно означает мутацию массива. Для изменений состава нужен var. Мы подробно будем менять массивы в следующих лекциях, но привычку «подумать про let/var заранее» стоит формировать уже сейчас.
Ошибка №5: давать массиву неинформативное имя и потом путаться, что внутри.
let a = ["Tom", "Ben"] компилируется, но через неделю вы забудете, что это: авторы, пользователи, теги? Хорошие имена (authors, usernames, tags) помогают читать код как текст — и это особенно важно, когда массивы начнут путешествовать по функциям и условиям.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ