More than one protocol in restriction type

I want to use a typical typed class and a type constraint:

class MyCustomClass<T : Equatable> { var a: Array<T> init() { a = Array<T>() } } 

It works great. But what happens if I want to use the second protocol, fe

 class MyCustomClass<T : Equatable, IndexableBase> { var a: Array<T> init() { a = Array<T>() } } 

It says that the initializer fails because I have to use 2 instead of 1 argument. I don’t get it.

+6
source share
2 answers

you can use this workaround

 class MyCustomClass<T: Equatable where T: IndexableBase > { var a: Array<T> init() { a = Array<T>() } } 
+1
source

Swift 3

(I replaced IndexableBase with Collection in your example, you should prefer the latter)


According to Swift 3, the infix & operator is used as part of the protocol as compared to the previous protocol<...> construct, as described in the accepted and implemented evolution project:

Therefore, using protocol composition, you can put a combined type constraint in the T placeholder in the parameter list:

 class MyCustomClass<T: Equatable & Collection> { /* ... */ } 

As an alternative to protocol composition, you can also use the where clause to combine several type (and subtype) constraints. In accordance with the accepted and implemented evolution proposal:

where clause is moved to the end of the declaration, in which case your example will read:

 class MyCustomClass<T: Equatable> where T: Comparable { /* ... */ } 

or even place a complete protocol composition with a where clause at the end of the declaration

 class MyCustomClass<T> where T: Equatable & Comparable { /* ... */ } 

What style do we prefer?

An interesting discussion is what we should consider as β€œbest practice,” as this specific issue is not listed in the Swift API guidelines. We host

  • all (main type) in the parameter list or
  • all restrictions at the end of the ad or
  • share the restrictions with some in the parameter list and others placed at the end of the declaration?

Consider the following contrived example, where we build a protocol that we plan to use as a type constraint ( Doable ), which in itself contains an associatedtype to which we can put a type constraint.

 protocol Doable { associatedtype U } 

Using the various methods above, all three of the following alternatives are valid, syntactically

 // alternative #1 func foo<T: Equatable & Doable>(_ bar: T) -> () where TU: Comparable { /* ... */ } // alternative #2 func foo<T: Equatable>(_ bar: T) -> () where T: Doable, TU: Comparable { /* ... */ } // alternative #3 func foo<T>(_ bar: T) -> () where T: Equatable & Doable, TU: Comparable { /* ... */ } 

I will cite Apple dev Joe Groff from the evolutionary stream that caused SE-0081 :

This is a call to judgment. It is my feeling that in many cases the general parameter is limited to at least one important protocol or base class that should be brought to the fore, so it’s wise to allow things like

 func foo<C: Collection>(x: C) -> C.Element 

without driving the Collection restriction too far from the front of the declaration.

Thus, in the above example, it may be advisable to use the composition of the protocol for type restrictions that apply directly to the generic placeholder T , and place these restrictions in the parameter list, while the restriction of the TU subtype is placed at the end of the declaration. Namely, alternative No. 1 above.

+14
source

Source: https://habr.com/ru/post/980114/


All Articles