domin
domin

Reputation: 1314

Swift protocols with generic arguments

Swift noob here. Consider this Swift 5.7 code:

import Foundation

// This is according to the grammar.
protocol TestProtocol1 {
    associatedtype T
    associatedtype U
}

// Not allowed by the grammer, but still compiles.
protocol TestProtocol2<T> {
    associatedtype T
    associatedtype U
}

// Doesn't seem to matter if I add one or both type arguments.
protocol TestProtocol3<T, U> {
    associatedtype T
    associatedtype U
}

// This is fine. As expected.
class TestClass1 : TestProtocol1 {
    typealias T = Int
    typealias U = Bool
}

// Fine too. Even though I don't specify the type arguments.
class TestClass2 : TestProtocol2 {
    typealias T = Int
    typealias U = Bool
}

// error: cannot inherit from protocol type with generic argument 'TestProtocol3<Int, Bool>'
class TestClass3 : TestProtocol3<Int, Bool> {
    typealias T = Int
    typealias U = Bool
}

Questions:

Upvotes: 2

Views: 2700

Answers (3)

Omer Tekbiyik
Omer Tekbiyik

Reputation: 4754

For the first question Is there any semantic difference between the three protocol definitions?

I dont think so . When you create a protocol with <..> after protocol name , Protocols think that the name giving between <> is a associated type or types but you must add that associatedtype name with the same in <>

For example if you delete U TestProtocol3 like

protocol TestProtocol3<T, U>{
    associatedtype T
}

you will get an error says : An associated type named 'U' must be declared in the protocol 'TestProtocol3' or a protocol it inherits.In the opposite way if you delete U in protocol TestProtocol3<T, U> like protocol TestProtocol3<T> , will not give an any error.

Upvotes: 2

Sweeper
Sweeper

Reputation: 271885

The "generic parameters" that you are seeing are the protocols' primary associated types, proposed in SE-0346, implemented in Swift 5.7, I suppose the grammar section in the language reference just hasn't been updated yet.

The three protocol declarations are semantically different, in that they have different primary associated types. When using the protocol in certain positions, primary associated types are what you can directly specify in <...>, rather than specify them somewhere else like in a where clause. For example, when using the protocol as a generic constraint:

func foo<P: TestProtocol2<Int>>(p: P) { ... }

is syntactic sugar for:

func foo<P: TestProtocol2>(p: P) where P.T == Int { ... }

The former is just a little more concise :)

You cannot do something similar with TestProtocol1, because it doesn't have primary associated types.

For TestProtocol3, you must specify both primary associated types:

func foo<P: TestProtocol3<Int, Bool>>(p: P) { ... }

According to the SE proposal, this syntax was also planned to be usable in the protocol conformance clause of a concrete type, like in your code:

class TestClass3 : TestProtocol3<Int, Bool> { // does not compile

However, this feature did not get added for some reason. You can still use it in the inheritance clause of a protocol though:

protocol TestProtocol4: TestProtocol3<Int, Bool> { } // works

See the SE proposal for more details.

Upvotes: 3

BEN MESSAOUD Mahmoud
BEN MESSAOUD Mahmoud

Reputation: 736

Here is my answer to your questions:

Is there any semantic difference between the three protocol definitions?

The only difference it's that TestProtocol1 has the correct declaration.

Why does it compile when declaring the associated types as generic arguments when the grammar doesn't allow it?

I made the test and it doesn't compile for me! kind weird 🤔

Why is it useful to (probably redundantly) add type arguments to protocols? For example, protocol Sequence does this too.

I'm not sure that I understand your question here but, the Sequence protocol is declared in the same way you had declared your TestProtocol1

/// A sequence should provide its iterator in O(1). The `Sequence` protocol
/// makes no other requirements about element access, so routines that
/// traverse a sequence should be considered O(*n*) unless documented
/// otherwise.
public protocol Sequence {

    /// A type representing the sequence's elements.
    associatedtype Element where Self.Element == Self.Iterator.Element

    /// A type that provides the sequence's iteration interface and
    /// encapsulates its iteration state.
    associatedtype Iterator : IteratorProtocol

    ...
}
``

Upvotes: 0

Related Questions