I would like to define a struct with automatic conformance to Encodable, where the struct has an array of Encodable elements:
struct EncodableBagB: Encodable {
var values: [Encodable] // ❌ Type 'EncodableBagB' does not conform to protocol 'Encodable'
}
If we take a step back and not use an array, the way to solve this is to move Encodable to be a generic:
struct EncodableBagA1<T: Encodable>: Encodable {
var value: T // Okay, but doesn't work for arrays
}
If I do the same with an array, I lose the ability to store heterogenous elements:
struct EncodableBagB1<T: Encodable>: Encodable {
var values: [T]
}
EncodableBagB1(values: ["A", "B"])
EncodableBagB1(values: ["A", "B", 1]) // ❌ Type of expression is ambiguous without more context
The only workaround seems to be to introduce a ton of duplicate generic structures:
struct EncodableBagB1_2<T0: Encodable, T1: Encodable, T2: Encodable, T3: Encodable>: Encodable {
var value0: T0
var value1: T1
var value2: T2
var value3: T3
init(_ encodable0: T0, _ encodable1: T1, _ encodable2: T2, _ encodable3: T3) {
value0 = encodable0
value1 = encodable1
value2 = encodable2
value3 = encodable3
}
// Okay, but very verbose and requires a bunch of duplicate structures (e.g. T0, T0-1, T0-2, T0-3, etc.) for each permutation
}
Can Swift 5.7's some
or any
keywords help in this situation, or are there any upcoming proposals which would eventually address this problem? What is this problem called (so I know what to look for in the future)?
I'd prefer to avoid manual type erasure (e.g. AnyEncodable
) if possible, since I'd like to use Mirror
on the items within the array.
Note: I'm using Encodable
to simplify the example. The actual protocol I'm eventually using looks more like this: protocol Foo: Encodable { func bar() }
.
Here is an exhaustive list of everything I tried:
// MARK: Single value
struct EncodableBagA: Encodable {
var value: Encodable // ❌ Type 'EncodableBagA' does not conform to protocol 'Encodable'
}
struct EncodableBagA1<T: Encodable>: Encodable {
var value: T // Okay, but doesn't work for arrays
}
struct EncodableBagA2: Encodable { // ❌ Type 'EncodableBagA2' does not conform to protocol 'Encodable'
var value: any Encodable
}
struct EncodableBagA3: Encodable {
var value: some Encodable // ❌ Property declares an opaque return type, but has no initializer expression from which to infer an underlying type
}
// MARK: Array
struct EncodableBagB: Encodable { // ❌ Type 'EncodableBagB' does not conform to protocol 'Encodable'
var values: [any Encodable]
}
struct EncodableBagB1<T: Encodable>: Encodable {
var values: [T]
}
EncodableBagB1(values: ["A", "B"])
EncodableBagB1(values: ["A", "B", 1]) // ❌ Type of expression is ambiguous without more context
struct EncodableBagB2<T: Encodable>: Encodable {
var values: [any T] // ❌ 'any' has no effect on type parameter 'T'
}
struct EncodableBagB3<T: Encodable>: Encodable { // ❌ Type 'EncodableBagB3' does not conform to protocol 'Encodable'
var values: [some T] // ❌ An 'opaque' type must specify only 'Any', 'AnyObject', protocols, and/or a base class
}
struct EncodableBagB1_1<T0: Encodable, T1: Encodable, T2: Encodable, T3: Encodable>: Encodable {
var value0: T0
var value1: T1
var value2: T2
var value3: T3
init(_ encodables: Encodable...) {
value0 = encodables[0] // ❌ Cannot assign value of type 'any Encodable' to type 'T0'
value1 = encodables[1] // ❌ Cannot assign value of type 'any Encodable' to type 'T1'
value2 = encodables[2] // ❌ Cannot assign value of type 'any Encodable' to type 'T2'
value3 = encodables[3] // ❌ Cannot assign value of type 'any Encodable' to type 'T3'
}
}
struct EncodableBagB1_2<T0: Encodable, T1: Encodable, T2: Encodable, T3: Encodable>: Encodable {
var value0: T0
var value1: T1
var value2: T2
var value3: T3
init(_ encodable0: T0, _ encodable1: T1, _ encodable2: T2, _ encodable3: T3) {
value0 = encodable0
value1 = encodable1
value2 = encodable2
value3 = encodable3
}
// Okay, but very verbose and requires a bunch of duplicate structures (e.g. T0, T0-1, T0-2, T0-3, etc.) for each permutation
}