Create an Array of Protocols with Constrained Associated Types

How can you store an array of protocols with associated types in Swift without using Any?

PATs are usually complex and do not involve an easier solution. I would suggest you come up with some simpler design for your problem. The problem with your method is actually having PAT and nested protocols and trying to make them work together.

Even if you type erase Action to some type like AnyAction, these two different types DogAction and CatAction again produce different types and you cannot intermix them inside the array. What you can possibly do is have two type erasures, one for Action and other for StateType. When you wrap CatAction or DogAction inside AnyAction both of them would then wrap in type erase state to AnyState.

Here is one example you could approach this type erasing both the protocols,

protocol StateType { }

struct DogState: StateType { }

struct CatState: StateType { }

protocol Action {
associatedtype ST: StateType
func process() -> ST
}

struct DogAction: Action {
func process() -> DogState { return DogState() }
}

struct CatAction: Action {
func process() -> CatState { return CatState() }
}

struct AnyState: StateType {
let state: StateType

init(_ state: StateType) {
self.state = state
}
}

struct AnyAction: Action {
typealias ST = AnyState

let processor: () -> AnyState

init<T: Action>(_ a: T) {
self.processor = {
return AnyState(a.process())
}
}

func process() -> AnyState {
return processor()
}
}

let cat = AnyAction(CatAction())
let dog = AnyAction(DogAction())

let actions = [cat, dog]

actions.forEach { action in
action.process()
}

I still think that you should rethink your solution, this can get more complicated as your types increase.

Swift add constraint extension to Protocol that has an associated type

What happens here is that Swift will gladly satisfy protocol requirements, if it can extract them from the context.

For example, assuming the extension would not exist, and the Doo definition would be like this:

struct Doo: Arr {
func node(_ at: Int) -> String? {
nil
}
}

, the the Swift compiler will happily fill the Element associated type.

A similar thing happens with your first snippet of code, Doo conforms to Arr, and the compiler finds a definition that satisfies all protocol requirements. Swift doesn't ignore the Element == String constraint, because it associates it with the Doo struct.

If you would add a second extension in a similar fashion, but for another type (an Int for example), you'll see that you receive the expected error. This happens because the compiler can no longer infer the protocol requirements.

The Swift compiler eagerly infers as much as possible from the context it can reach to, most of the time gives great results, sometimes not so great (especially when working with closures), and sometimes it gives unexpected results (like this one).

If you want to be sure that the compiler infers the types you want, a solution would be to explicitly declare all involved types.

Can't create an Array of types conforming to a Protocol in Swift

Let's say, if we could put an instance of Thing into array foos, what will happen?

protocol Foo {
associatedtype BazType
func bar(x:BazType) -> BazType
}

class Thing: Foo {
func bar(x: Int) -> Int {
return x.successor()
}
}

class AnotherThing: Foo {
func bar(x: String) -> String {
return x
}
}

var foos: [Foo] = [Thing()]

Because AnotherThing conforms to Foo too, so we can put it into foos also.

foos.append(AnotherThing())

Now we grab a foo from foos randomly.

let foo = foos[Int(arc4random_uniform(UInt32(foos.count - 1)))]

and I'm going to call method bar, can you tell me that I should send a string or an integer to bar?

foo.bar("foo") or foo.bar(1)

Swift can't.

So it can only be used as a generic constraint.

What scenario requires a protocol like this?

Example:

class MyClass<T: Foo> {
let fooThing: T?

init(fooThing: T? = nil) {
self.fooThing = fooThing
}

func myMethod() {
let thing = fooThing as? Thing // ok
thing?.bar(1) // fine

let anotherThing = fooThing as? AnotherThing // no problem
anotherThing?.bar("foo") // you can do it

// but you can't downcast it to types which doesn't conform to Foo
let string = fooThing as? String // this is an error
}
}

More than one protocol in a type constraint

you can use this workaround

class MyCustomClass<T: Equatable where T: IndexableBase > {
var a: Array<T>
init() {
a = Array<T>()
}
}

Swift 4:

class MyCustomClass<T: Equatable> where T: Collection  {
var a: Array<T>
init() {
a = Array<T>()
}
}

Protocol conforming to type with associated value

Because this is not a current feature of Swift. Once there is an associated type, there is always an associated type. It doesn't go away just because you constrain it. And once it has an associated type, it is not concrete.

There is no way to "inherit" protocols this way. What you mean is:

protocol MyProtocol {
var id: UUID { get }
}

And then you can attach Identifiable to structs that require it:

struct X: MyProtocol, Identifiable {
var id: UUID
}

(note that no where clause is required.)

There is no Swift feature today that allows you to say "types that conform to X implicitly conform to Y." There is also no Swift feature today that allows for an Array of "things that conform to Identifiable with ID==UUID." (That's called a generalized existential, and it's not currently available.)

Most likely you should go back to your calling code and explore why you require this. If you post the code that iterates over test and specifically requires the Identifiable conformance, then we may be able to help you find a design that doesn't require that.

swift protocol method when associated meets a constraint

To make @matt's comment concrete, this is in fact two protocols with different constraints. So give those different things different names:

public protocol ListOfThings {
associatedtype ThingType

func append(_ thing: ThingType)
}

// This requires that anything conforming to RemovableListOfThings
// also conform to ListOfThings, and that ThingType conform to Equatable,
// and that the type also implements `remove`.
public protocol RemovableListOfThings: ListOfThings
where ThingType: Equatable {
func remove(_ thing: ThingType)
}

What you're trying to do can't work since a type might be conformed to Equatable in another module. The compiler can't go back and revoke the conformance to ListOfThings just because the type is later conformed to Equatable (thus changing the requirements retroactively).



Related Topics



Leave a reply



Submit