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
What's The Difference Between [String] VS. [(String)]
How to Keep a User Logged In? Swift
Get Output Frames Failed, State 8196
Sbdata Is Wrong When Sbvalue Comes from a Swift Dictionary
What Does an Underscore "_" Mean in Swift
Save/Copy a File from Bundle to Desktop Using Nssavepanel
How to Correctly Use Shouldcompactonlaunch in Realmswift
Using Associatedtype in a Delegate Protocol for a Generic Type
How to Decode a Utf16 String into a Unicode Character
Navigationlink Inside .Searchable Does Not Work
Migrate from Racsignal to Reactiveswift or Rac5
Swift: Make Two Types with The Same "Shape" Conform to a Common Protocol
Ibdesignable View Not Rendering
How Are Swift Enums Implemented Internally
Error Loading Media Sources in Mlmedialibrary
Errors When Update Code to Avoid Deprecation Warnings Withunsafemutablebytes in Swift 5