#swift #string #compiler-errors
#swift #строка #ошибки компилятора
Вопрос:
Следующее определение функции является законным для Swift:
func doSomething<T: StringProtocol>(value: T = "abc") {
// ...
}
Компилятор способен определить, что аргументом по умолчанию "abc"
является String
и String
соответствует StringProtocol
.
Однако этот код не компилируется:
func doSomething<T: Collection>(value: T = "abc") where T.Element == Character {
// ...
}
Ошибка компилятора:
Значение аргумента по умолчанию типа ‘String’ не может быть преобразовано в тип ‘T’
Кажется, что компилятор будет располагать таким же количеством информации, как и в первом случае, чтобы определить, что String
действительно конвертируемо в T
. Более того, если я удалю аргумент по умолчанию и вызову функцию с тем же значением, это сработает:
doSomething(value: "abc")
Можно ли написать эту функцию по-другому, чтобы я мог предоставить аргумент по умолчанию String
? Это ограничение Swift или просто ограничение моей ментальной модели?
Комментарии:
1. Я понятия не имел, что вы можете это сделать; спасибо, что спросили!
Ответ №1:
Существенным ограничением является T: ExpressibleByStringLiteral
. Это то, что позволяет инициализировать что-либо из строкового литерала.
func doSomething<T: Collection>(value: T = "abc")
where T.Element == Character, T: ExpressibleByStringLiteral {
// ...
}
Как отмечает Лео Дабус, T.Element == Character
технически в этом нет необходимости, но его удаление меняет смысл. То, что что-то является коллекцией и может быть инициализировано строковым литералом, не означает, что его элементы являются символами.
Также стоит отметить, что, хотя все это возможно, обычно это плохой Swift IMO. У Swift нет никакого способа выразить, каков тип по умолчанию, поэтому doSomething()
во всех этих случаях возникает «Общий параметр ‘T’ не может быть выведен».
Правильное решение IMO — это перегрузка, которая позволяет избежать всех этих проблем:
func doSomething<T: StringProtocol>(value: T) {
}
func doSomething() {
doSomething(value: "abc")
}
Это позволяет вам сделать параметр по умолчанию не просто «чем-то, что может быть инициализировано с помощью литерала "abc"
«, а тем, что вы на самом деле имеете в виду: значением по умолчанию является строка «abc».
Как правило, параметры по умолчанию — это просто удобства для перегрузок, поэтому обычно вы можете заменить любой параметр по умолчанию явной перегрузкой, в которой этот параметр отсутствует.
Комментарии:
1. @LeoDabus Я не понимаю, как это повлияет на ситуацию с параметрами по умолчанию. Как бы вы реализовали синтаксис
doSomething()
в качестве расширения в RangeReplaceableCollection?2. Я думаю, что подход с перегрузкой — это то, что я ищу. Возможно, стоит упомянуть, что мой реальный вариант использования немного сложнее и на самом деле не включает строки, но обсуждение здесь, тем не менее, очень содержательное!