Reputation: 10124
I'm trying to declare a function in a protocol that forces types that conform to it to return a value of that same protocol but with a specific associated type:
protocol Protocol {
typealias ValueType
var value : ValueType? {get}
func getProtocolString<A where A : Protocol, A.ValueType == String>() -> A
}
This compiles. It's when I try to create a class that conforms to it that I get the errors:
class AClass<T> : Protocol {
var value : T?
func getProtocolString<A where A : Protocol, A.ValueType == String>() -> A {
return AClass<String>()
}
}
The error is 'AClass' is not convertible to 'A'.
Am I missing something? Is this even possible?
Thank you
Upvotes: 5
Views: 324
Reputation: 40973
The problem lies with confusing a generic placeholder constrained by a protocol, with the protocol itself. Here’s a simpler example, similar to your code, to try and make it clear:
// first, define a protocol and two structs that conform to it
protocol P { }
struct S1: P { }
struct S2: P { }
// now, a function that returns an object in the form
// of a reference to protocol P
func f() -> P {
// S1 conforms to P so that’s fine
return S1()
}
// ok all well and good, this works fine:
let obj = f()
// now, to do something similar to your example code,
// declare a generic function that returns a generic
// placeholder that is _constrained_ by P
// This will NOT compile:
func g<T: P>() -> T { return S1() }
Why does this not compile?
The way generic functions work is that at compile time, when you call the function, the compiler decides what type the placeholder T
needs to be, and then writes you a function with all occurrences of T
replaced with that type.
So with the example below, T
should be replaced by S1
:
let obj1: S1 = g()
// because T needs to be S1, the generic function g above is
// rewritten by the compiler like this:
func g() -> S1 { return S1() }
This looks OK. Except, what if we wanted T
to be S2
? S2
conforms to P
so is a perfectly legitimate value for T
. But how could this work:
// require our result to be of type S2
let obj2: S2 = g()
// so T gets replaced with S2… but now we see the problem.
// you can’t return S1 from a function that has a return type of S2.
// this would result in a compilation error that S2 is not
// convertible to S1
func g() -> S2 { return S1() }
Here is the origin of the error message you are getting. Your placeholder A
can stand in for any type that conforms to Protocol
, but you are trying to return a specific type (AClass
) that conforms to that protocol. So it won’t let you do it.
Upvotes: 2
Reputation: 12868
This seemed to work in the playground... does it work for what you are trying to do?
protocol StringProtocol
{
typealias ValueType
var value : ValueType? { get }
func getProtocolString<A where A: StringProtocol, A.ValueType == String>() -> A
}
class StringClass : StringProtocol
{
typealias ValueType = String
var value : ValueType?
init() { }
func getProtocolString<A where A: StringProtocol, A.ValueType == String>() -> A
{
return StringClass() as A
}
}
I'm still not exactly following what requirements you are trying to fulfill with this implementation.
Upvotes: 0
Reputation: 2193
It seems that you are a little misunderstanding generics. Generic functions are instantiated at call sites of these, not at each function itself body. So, the type constraints you've written are saying that this function returns a value, a type of which may be any of all the subtypes of the Protocol
. Consequently, the function definition must be statically correct about A
for all the subtypes of Protocol
, not only for AClass<String>
, which is only an one type of Protocol
.
In any case, I think there is no direct way to achieve what you want, at least in current Swift.
Upvotes: 0