Related Protocol Types and Generics

I am trying to declare a function in a protocol that forces the types corresponding to it to return the value of the 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 } 

It compiles. This is when I try to create a class that matches it, that I get errors:

 class AClass<T> : Protocol { var value : T? func getProtocolString<A where A : Protocol, A.ValueType == String>() -> A { return AClass<String>() } } 

Error: "AClass" does not convert to "A".

Am I missing something? Is it possible?

thanks

+5
source share
3 answers

The problem is confusing the common placeholder, limited by the protocol, with the protocol itself. Here is a simple example similar to your code to make it clearer:

 // 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 is this not compiling?

How common functions work, during compilation, when you call a function, the compiler decides what type the placeholder T should be and then writes you a function with all the occurrences of T replaced by that type.

So, in 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() } 

It looks ok. Also, what if we want T be S2 ? S2 corresponds to P , so this is an absolutely legal 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 source of the error message you receive. Your placeholder A may be present for any type that conforms to Protocol , but you are trying to return a specific type ( AClass ) that conforms to this protocol. So this will not allow you to do this.

+2
source

It seems that you are a little misunderstanding the generics. Common functions are created on call sites, not on each one. Thus, the type restrictions that you wrote suggest that this function returns a value whose type can be any of all Protocol subtypes. Therefore, the function definition must be statically correct with respect to A for all Protocol subtypes, and not just for AClass<String> , which is only one type of Protocol .

In any case, I think that there is no direct way to achieve what you want, at least in the current Swift.

0
source

It seemed to work on the playground ... does it work on 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 still do not quite understand what requirements you are trying to fulfill with this implementation.

0
source

Source: https://habr.com/ru/post/1215334/


All Articles