Why the usage of base protocol in conformance to the protocol type is not allowed?

As I wrote earlier Sub: P (could) work, because General conforms to A.

I don't know what this means. Sub: P is how the code in the original post is written. It's still the case that init(_ a: A) does not meet the requirement of P.

I explained it here: Why the usage of base protocol in conformance to the protocol type is not allowed? - #14 by Lantua. init(_: A) (could) meet the requirement of init(_: Generic) because Generic conforms to A. Whether or not A conforms to Generic is utterly irrelevant.

I had to read the post a couple times and work out with pseudocode how it would work. Thanks for expanding my understanding.

1 Like

So all-in-all, there's nothing wrong from the type perspective. I believe it fails because Swift currently requires that the type of the methods match exactly with the protocol requirement, not even its subtype.

Not yet, but you can easily make it wrong.

class Sub: P {
  init(_ a: A) {
    
  }
  
  init(_: B) {
    
  }
}

Easy solution, though.

required convenience init(_ general: General) {
  self.init(general as A)
}

I was referring to Sub.init — but actually, nothing forces you to actually use methods from B there, so I have to agree that technically, everything should be fine (that is: You can create an instance with a parameter of type General, thus fulfil what the protocol demands).

However, I'm not sure if I would like to see this restriction lifted:
There is no strict rule that signatures have to match exactly when you implement a protocol, but doing so avoids confusion; I'd rather write a small convenience initialiser and have a connection to the protocol which is visible at first sight.

It is lifted already with generics, see this post Allow protocol conformance by declaring a method with a supertype argument - #2 by xwu
So no reason to keep it with existentials.

To write the convenience init or not should be the choice of the developer and a style guide the developer is using.

That's not the same thing; there's no multiple inheritance there that would require potentially-impossible disambiguation.

You can't write a convenience init to disambiguate unless you're using existentials, because disambiguation between type cousins requires casting.

Edit: Scratch that. Opaque types opened this up.

class Sub: P {
  init<T: A>(_ a: T) {

  }

  init<T: B>(_ b: T) {

  }
  
  required convenience init<T: General>(_ general: T) {
    let a: some A = general
    self.init(a)
  }
}