Protocol with a where clause vs protocol inheritance

Is there a difference between B & C below or are they equivalent?

protocol A {}
protocol B where Self : A {}
protocol C: A {}
2 Likes

Intuitively, I would say they are not the same:

  • C inherits A (protocol inheritance)
  • B does not inherit A, but puts a constraint on which types can adopt it.

B does seem to inherit A, as the following code demonstrates.

protocol A {}
protocol B where Self : A {}
protocol C: A {}

struct Foo: B {
}

func fubar (_ p: A) {
    print (type (of: p))
}

let u = Foo ()
let p: A = u

fubar (u) // prints Foo
fubar (p) // prints Foo

It appears that protocol conformance only requires a protocol's requirements to be satisfied, not that a type explicitly adopts it (by placing the protocol’s name after the type’s name).

Actually, the docs say so: " Any type that satisfies the requirements of a protocol is said to conform to that protocol." So conforming to a protocol and adopting a protocol are not the same.

protocol A {
    func a()
}

protocol B where Self : A {
    func b()
}

struct Foo: B { // gives: Type 'Foo' does not conform to protocol 'A' if func a() is not present
    func b() { }
    func a() { }
}

Adding a bit to my first answer: I think as types B and C are not the same, a C is-an A, wheres a B is-not-an A, that would be my thinking here.

1 Like

Thank you for clarifying the subtlety.

How exactly could you check this? C is A is a compilation error :slight_smile:

1 Like

They are equivalent. Each of the following pairs are equivalent:

protocol P: Q {}
protocol P where Self: Q {}

func f<T: P>(_: T) {}
func f<T>(_: T) where T: P {}

associatedtype A: P
associatedtype A where Self.A: P

The desugaring is documented in Chapter 4 of swift/docs/Generics at main · swiftlang/swift · GitHub. To build it, install MacTeX and run make in docs/Generics.

5 Likes

You can check that a type parameter constrained to C satisfies a conformance requirement to A or whatever:

func f<T: C>(_ t: T) { g(t) }
func g<T: A>(_: T) {}