Как перестать бояться deprecated и полюбить slicing в Swift
Если вы когда-нибудь писали код на Swift и пытались вырезать часть строки, вы наверняка сталкивались с этим пугающим сообщением: 'substring(to:)' is deprecated: Please use String slicing subscript with a 'partial range from' operator. Звучит как приговор? На самом деле это — приглашение в новый, более безопасный и мощный мир работы со строками. Давайте разберемся, что произошло, почему Apple решила изменить привычный API и как теперь правильно «резать» строки в Swift 4 и новее.
Почему substring ушел в прошлое?
Старая добрая substring(to:) работала, но была... не очень умной. Она возвращала новый объект Substring, который, по сути, был просто «окошком» в исходную строку. Это приводило к путанице: вы думали, что работаете с самостоятельной строкой, а на самом деле держали ссылку на старую память. В Swift 4 решили навести порядок: теперь все операции с подстроками возвращают тип Substring, а для получения полноценной строки нужно явно конвертировать результат. Звучит сложнее? На практике — гораздо понятнее и безопаснее.
Знакомьтесь: String slicing subscript
Новый подход использует сабскрипты (subscripts) и диапазоны (ranges). Вместо методов substring(to:), substring(from:) и substring(with:) вы теперь пишете код, который выглядит как работа с массивами. Вот базовая аналогия:
let fullString = "Привет, мир!"
// Старый способ (deprecated)
// let oldResult = fullString.substring(to: fullString.index(fullString.startIndex, offsetBy: 6))
// Новый способ
let newResult = fullString[..<fullString.index(fullString.startIndex, offsetBy: 6)]
print(newResult) // "Привет"
Видите оператор ..<? Это и есть тот самый partial range from operator — частичный диапазон «от начала до указанного индекса, не включая его». Мощно? Ещё бы.
Три кита нового подхода: from, to, through
Давайте разберем три основных сценария, которые раньше покрывали методы substring.
1. substring(to:) → prefix(upTo:) или частичный диапазон
Хотите взять всё до определённого индекса? Используйте ..< (half-open range) или метод prefix(upTo:):
let str = "Hello, Swift"
let index = str.firstIndex(of: ",") ?? str.endIndex
// Вариант через сабскрипт
let result1 = str[..<index]
print(result1) // "Hello"
// Вариант через метод
let result2 = str.prefix(upTo: index)
print(result2) // "Hello"
2. substring(from:) → suffix(from:) или частичный диапазон
Нужно взять всё от индекса до конца? Используйте ... (closed range) с опущенным началом:
let startIndex = str.index(str.startIndex, offsetBy: 7)
let result3 = str[startIndex...]
print(result3) // "Swift"
// Или через метод
let result4 = str.suffix(from: startIndex)
print(result4) // "Swift"
3. substring(with:) → обычный диапазон
Хотите вырезать конкретный кусок? Просто укажите диапазон:
let range = str.index(str.startIndex, offsetBy: 7)..<str.endIndex
let result5 = str[range]
print(result5) // "Swift"
// Или через closed range
let closedRange = str.index(str.startIndex, offsetBy: 7)...str.index(before: str.endIndex)
let result6 = str[closedRange]
print(result6) // "Swift"
Обратите внимание: все эти операции возвращают Substring, а не String. Если вам нужна полноценная строка, просто оберните результат в String():
let newString = String(result1)
print(type(of: newString)) // String
А что с prefix и suffix? Они же не deprecated!
Да, методы prefix(_:), suffix(_:), prefix(upTo:), suffix(from:) остались и активно используются. Они удобны, когда нужно взять несколько символов от начала или конца. Но для точной работы с произвольными индексами сабскрипты — ваш выбор.
Вот таблица для быстрой справки (запомните её, она спасёт вас на собеседовании):
- Старый метод
substring(to:)→ Новый способstr[..<index]илиstr.prefix(upTo:) - Старый метод
substring(from:)→ Новый способstr[index...]илиstr.suffix(from:) - Старый метод
substring(with:)→ Новый способstr[range]
Практический пример: разбор строки с данными
Представьте, что вы парсите ответ от сервера: "user123:John:Doe:1990". Вам нужно извлечь ID пользователя (первые 7 символов) и год рождения (последние 4 символа). Вот как это делается по-новому:
let data = "user123:John:Doe:1990"
// Извлекаем ID (первые 7 символов)
let idSubstring = data[..<data.index(data.startIndex, offsetBy: 7)]
let userId = String(idSubstring)
print("User ID: \(userId)") // "user123"
// Извлекаем год (последние 4 символа)
let yearSubstring = data[data.index(data.endIndex, offsetBy: -4)...]
let birthYear = String(yearSubstring)
print("Birth year: \(birthYear)") // "1990"
// А теперь извлечём имя (между первым и вторым двоеточием)
if let firstColon = data.firstIndex(of: ":"),
let secondColon = data[data.index(after: firstColon)...].firstIndex(of: ":") {
let nameSubstring = data[data.index(after: firstColon)..<secondColon]
let name = String(nameSubstring)
print("Name: \(name)") // "John"
}
Чувствуете разницу? Вместо цепочки substring(from:).substring(to:) мы используем чёткие индексы и диапазоны. Код стал читаемее и предсказуемее.
Типичные ошибки начинающих (и как их избежать)
Вот три грабли, на которые наступают почти все:
- Забывают конвертировать Substring в String. Если вы попытаетесь передать Substring туда, где ожидается String, компилятор не ругнётся, но могут возникнуть проблемы с производительностью (Substring хранит ссылку на исходную строку). Всегда делайте
String(yourSubstring). - Путают half-open и closed диапазоны
Блоги
Рекомендаций по книгам
Читайте также