Ограничения нескольких типов в Swift

133

Допустим, у меня есть эти протоколы:

protocol SomeProtocol {

}

protocol SomeOtherProtocol {

}

Теперь, если мне нужна функция, которая принимает общий тип, но этот тип должен соответствовать, SomeProtocolя мог бы сделать:

func someFunc<T: SomeProtocol>(arg: T) {
    // do stuff
}

Но есть ли способ добавить ограничение типа для нескольких протоколов?

func bothFunc<T: SomeProtocol | SomeOtherProtocol>(arg: T) {

}

В подобных вещах используются запятые, но в этом случае начнется объявление другого типа. Вот что я попробовал.

<T: SomeProtocol | SomeOtherProtocol>
<T: SomeProtocol , SomeOtherProtocol>
<T: SomeProtocol : SomeOtherProtocol>
логан
источник
Это особенно актуальный вопрос, поскольку в документации Swift об этом не упоминается в главе о дженериках ...
Бруно Филип

Ответы:

242

Вы можете использовать предложение where, которое позволяет указать столько требований, сколько вы хотите (все из которых должны быть выполнены), через запятую.

Свифт 2:

func someFunc<T where T:SomeProtocol, T:SomeOtherProtocol>(arg: T) {
    // stuff
}

Свифт 3 и 4:

func someFunc<T: SomeProtocol & SomeOtherProtocol>(arg: T) {
    // stuff
}

или более мощное предложение where:

func someFunc<T>(arg: T) where T:SomeProtocol, T:SomeOtherProtocol{
    // stuff
}

Вы, конечно, можете использовать состав протокола (например, protocol<SomeProtocol, SomeOtherProtocol>), но он немного менее гибкий.

Использование whereпозволяет работать со случаями, когда задействованы несколько типов.

Вы все равно можете захотеть составить протоколы для повторного использования в нескольких местах или просто дать составленному протоколу осмысленное имя.

Свифт 5:

func someFunc(arg: SomeProtocol & SomeOtherProtocol) { 
    // stuff
}

Это кажется более естественным, поскольку протоколы находятся рядом с аргументом.

Jiaaro
источник
Черт возьми, это не логично, но приятно знать, что я просто хочу поблагодарить спамеров за это, не понял этого через месяц, так как мне это было нужно.
Mathijs Segers
3
Любой способ сделать то же самое с классами и структурами в выражении с ограничением типов? например <T where T:SomeStruct, T:AnotherStruct>? Для классов компилятор, кажется, интерпретирует это как высказывание «T является подклассом обоих», а для структур он просто жалуется на это "Type 'T' constrained to non-protocol type".
Джаррод Смит
Для конкретного примера в OP: s состав протокола вопросов должен быть предпочтительным методом: приведенное выше решение действительно, но, imho, излишне загромождает подпись функции. Кроме того, использование композиции протокола в качестве, например, ограничения типа, по-прежнему позволяет вам использовать whereпредложение для дополнительного типа / другого использования, например, func someFunc<U, T: protocol<SomeProtocol, SomeOtherProtocol> where T.SubType == U>(arg: T, arg2: U) { ... }для псевдонимов типов SubTypeв, например SomeProtocol.
дпр 05
1
Похоже, это устарело в swift3 и рекомендует использовать: func someFunc <T> (arg: T), где T: SomeProtocol, T: SomeOtherProtocol {
Кристи Бэлуцэ
2
Есть ли способ сказать Swift, что T должен быть определенного типа объекта и реализовывать определенный протокол?
Георг
73

У вас есть две возможности:

  1. Вы используете предложение where, как указано в ответе Jiaaro:

    func someFunc<T where T : SomeProtocol, T : SomeOtherProtocol>(arg: T) {
        // do stuff
    }
  2. Вы используете тип композиции протокола :

    func someFunc<T : protocol<SomeProtocol, SomeOtherProtocol>>(arg: T) {
        // do stuff
    }
Жан-Филипп Пелле
источник
2
imo, второе решение красивее, я бы пошел на этот ответ, он также более полный, представляя два варианта
Mathijs Segers
2
Номер 2 - единственный, который у меня работает в Swift 2 при объявлении typealias. Спасибо!
Бруно Филип
19

Эволюция Swift 3.0 внесла некоторые изменения. Теперь два наших варианта выглядят немного иначе.

Использование whereпредложения в Swift 3.0:

Предложение whereтеперь перемещено в конец сигнатуры функции, чтобы улучшить читаемость. Итак, множественное наследование протоколов теперь выглядит так:

func someFunc<T>(arg: T) where T:SomeProtocol, T:SomeOtherProtocol {

}

Использование protocol<>конструкции в Swift 3.0:

Композиция с использованием protocol<>конструкции устарела. Ранее protocol<SomeProtocol, SomeOtherProtocol>теперь выглядит так:

func someFunc<T:SomeProtocol & SomeOtherProtocol>(arg: T) {

}

Ссылки.

Подробнее об изменениях для whereздесь: https://github.com/apple/swift-evolution/blob/master/proposals/0081-move-where-expression.md

И подробнее об изменениях для конструкции protocol <> можно прочитать здесь: https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.md

ncke
источник
13

Swift 3 предлагает до 3 различных способов объявления вашей функции.

protocol SomeProtocol {
    /* ... */
}

protocol SomeOtherProtocol {
    /* ... */        
}

1. Использование &оператора

func someFunc<T: SomeProtocol & SomeOtherProtocol>(arg: T) {
    /* ... */
}

2. Использование whereпредложения

func someFunc<T>(arg: T) where T: SomeProtocol, T: SomeOtherProtocol {
    /* ... */
}

3. Использование whereпредложения и &оператора

func someFunc<T>(arg: T) where T: SomeProtocol & SomeOtherProtocol {
    /* ... */        
}

Также обратите внимание, что вы можете использовать typealiasдля сокращения объявления функции.

typealias RequiredProtocols = SomeProtocol & SomeOtherProtocol

func someFunc<T: RequiredProtocols>(arg: T) {
    /* ... */   
}
Imanou Petit
источник