Why am I getting this conformance problem? - swift

protocol A {}
protocol B: A {}
protocol C {
var X: A { get }
}
struct D: C {
let X: B // ERROR: does not conform to protocol "C"
}
Shouldn't this be okay since B conforms with A?
I tried to remedy it by using an associatedtype in protocol C:
protocol A {}
protocol B: A {}
protocol C {
associatedtype SomeType: A
var X: SomeType { get }
}
struct D: C {
let X: B // ERROR: does not conform to protocol C -- seemingly requiring a concrete type
}

The error makes sense if you think about it the following way.
/// It does not have any requirements
protocol A {}
/// It has to satisfy all of the requirements of A (whatever they are)
/// It does not have any of it's own requirements (currently)
/// It is expected to add some additional requirements of it's own
/// Otherwise it defeats the purpose of having this
protocol B: A {}
/// This has only one requirement, X that conforms to A
protocol C {
var X: A { get }
}
/// This has only one requirement, X that conforms to B
/// The problem here is
/// - A & B are different and can not be used interchangeably
/// (at least in the current versions)
struct D: C {
let X: B // ERROR: does not conform to protocol "C"
}
Logically it makes sense - that B has to satisfy all requirements of A and hence it should be allowed.
I think Swift Forums might be a better place for discussing this.

Related

Is it possible to declare a protocol with an associatedtype inside a protocol?

I want to declare a protocol with an associatedtype inside the protocol.
I know that declaring it as a class rather than a protocol solves the problem.
But I want to use it within the protocol.
Is there a way to use a protocol with an associated type in the protocol using things like generics or typealias?
protocol A {
associatedtype T
}
protocol B {
var a: A { get } // error. protocol 'A' can only be used as a generic constraint because it has Self or associated type requirements
// But I want set a.T = Int
}
If you really want to set A.T to Int, you can specify that in an associated type where clause within B:
protocol A {
associatedtype T
}
protocol B {
associatedtype U: A where U.T == Int
var a: U { get }
}
Or, you do not want to lock B to only one particular A.T type, you can introduce another associatedtype, which links back to A:
protocol A {
associatedtype T
}
protocol B {
associatedtype T
associatedtype U: A where U.T == T
var a: U { get }
}
See Associated Types with a Generic Where Clause in The Swift Programming Language.
protocol A {
associatedtype T
var a: T { get }
}
protocol B {
associatedtype U where U: A
var b: U { get }
}
Keep in mind though that you're gonna have to reason a lot about this because of the two associated types.

Swift - Differences between inheriting protocol and constraining Self to protocol

I have a protocol A:
protocol A { }
What are the differences between implementing protocol B like this:
protocol B: A { }
versus implementing it like this:
protocol B where Self: A { }
?
What can I do with one that I can't do with the other? Where will I run into a problem with one that I could fix by switching it to the other?
One difference is in how you conform to the protocol.
In the former case, a conformance to B implies a conformance to A.
struct BImpl: B {} // `A` is implied here
In the latter case, a conformance to B requires a conformance to A, but it doesn't implicitly create one:
struct BImpl: A, B {} // `A` is explicitly required here

How can a class conform to a protocol with a protocol constrained associated type? [duplicate]

This question already has answers here:
Unable to use protocol as associatedtype in another protocol in Swift
(2 answers)
Closed 4 years ago.
The question title is hard to digest but you'll see what I mean when you look at the code sample. I am trying to get class D to conform to protocol C by using protocol B as the type alias. I thought this would be ok, since B also conforms to A, which is the constraint defined in the associated type in C, but the compiler throws an error. Is what I'm trying to do not possible?
protocol A { }
protocol B: A { }
protocol C {
associatedtype T: A
}
class D: C {
typealias T = B
}
Note: it works if B is a class instead of a protocol.
Overview
Not sure if you really need associated type
Use the base protocol instead
Code
protocol A { }
protocol B: A { }
protocol C {
func f1(something : A)
}
class D : C {
func f1(something: A) { }
}
class X : B {}
let d1 = D()
d1.f1(something: X())

generic class as associated type

when I do:
class GenericClass<T> {}
protocol Protocol {
associatedtype A
associatedtype C : GenericClass<A>
}
class IntClass : GenericClass<Int> {}
struct Adopter : Protocol {
typealias A = Int
typealias C = IntClass
}
everything is ok 😎
but if I add a method to Protocol that uses the C associated type:
protocol Protocol {
...
func f(c: C)
}
and implement it in Adopter:
struct Adopter : Protocol {
...
func f(c: C) {}
}
everything brakes! The compiler tells me:
"Protocol requires function 'use(c:)' with type '(Adopter.C) -> ()'..."
My best guess is that this has something to do with the fact that C is a class and that the type signature f(c: C) would allow subclasses of C being passed but I can't see how that would be a problem...
Here's a gist illustrating the problem that you can paste into a playground
I'm wondering if this is a bug? I hope I'm not *again* fundamentally misunderstanding something with the type system 😅
update
I think I am getting to the root of the problem by just declaring the protocol like this:
protocol Protocol {
associatedtype A
associatedtype C : GenericClass<A>
func f(c: C)
}
putting this into a playground, I get the error:
Playground execution failed: error: generic parameter 'C' cannot be a subclass of both 'GenericClass<<< error type >>>' and 'GenericClass'
just because, I tried mimicking a similar type relationship with a struct:
struct Struct<
A,
C : GenericClass<A>
> {
func f(c: C) {}
}
This compiles just fine.

Protocol downcasting in Swift

I have the following situation where I have a generic protocol A that can have variants that inherit from it (e.g. B) and a class that implements a variant (C).
protocol A {}
protocol B: A {
var foo: String { get }
}
class C: B {
let foo: String = "foo"
}
Now, if I have an object of type A, but is actually a C, how can I get to stuff declared in B?
func foo() {
let c: A = C()
}
If I try to cast as in let b = c as B I get Cannot downcast from 'A' to non-#objc protocol type 'B'.
Your code looks good - the only problem is that the as operator requires objc compatibility. You can fix it by prefixing your protocols with the #objc attribute:
#objc protocol A {}
#objc protocol B: A {
var foo: String { get }
}
Unfortunately there's a downside on doing that: you lose the ability to use swift-specific features that are not available in objective C, such as enums, tuples, generics, etc.
More info here: Checking for Protocol Conformance