Как вы предоставляете аргумент по умолчанию для универсальной функции с ограничениями типов? - PullRequest
2 голосов
/ 30 марта 2019

Следующее определение функции: legal 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?Это ограничение Свифта или просто ограничение моей ментальной модели?

1 Ответ

2 голосов
/ 30 марта 2019

Значительным ограничением является T: ExpressibleByStringLiteral.Это то, что позволяет что-то инициализировать из строкового литерала.

func doSomething<T: Collection>(value: T = "abc")
    where T.Element == Character, T: ExpressibleByStringLiteral {
    // ...
}

Как отмечает Лео Дабус, T.Element == Character технически не требуется, но удаление его меняет смысл.То, что что-то является коллекцией и может быть инициализировано строковым литералом, не означает, что его элементы являются символами.

Стоит также отметить, что, хотя все это возможно, это, как правило, плохое Свифт ИМО.Swift не имеет никакого способа выразить, что является типом по умолчанию , поэтому doSomething() во всех этих случаях приводит к тому, что «общий параметр« T »не может быть выведен».

ПравильныйРешение IMO - это перегрузка, которая позволяет избежать всех этих проблем:

func doSomething<T: StringProtocol>(value: T) {
}

func doSomething() {
    doSomething(value: "abc")
}

Это позволяет вам сделать параметр по умолчанию не просто «чем-то, что может быть инициализировано литералом "abc"», а тем, что вы на самом делеозначают: значением по умолчанию является String "abc".

Как правило, параметры по умолчанию являются просто удобством для перегрузок, поэтому вы можете вообще заменить любой параметр по умолчанию явной перегрузкой, в которой отсутствует этот параметр.

...