solidcell solidcell - 21 days ago 9
Swift Question

Dependency inject a generic struct without having to re-specify placeholder type or constraints

I'm trying to dependency inject a generic struct into a class, but I don't want to have to re-specify the generic's placeholder type again in the class as well. That gets out of hand because any class that uses this class will have to do the same, on and on.

For instance, this works:

struct SomeStruct<T : CustomStringConvertible> {
let data: T
}

class SomeClass {
let someStruct = SomeStruct(data: 1)
}


However, if I want to dependency inject the
struct
into the
class
, I get an error:

struct SomeStruct<T : CustomStringConvertible> {
let data: T
}

class SomeClass {
let someStruct: SomeStruct
// ^
// Reference to generic type 'SomeStruct' requires arguments in <...>

init(someStruct: SomeStruct) {
// ^
// Reference to generic type 'SomeStruct' requires arguments in <...>
self.someStruct = someStruct
}
}


So then I have to specify the placeholder type and type constraints again in the
class
:

struct SomeStruct<T : CustomStringConvertible> {
let data: T
}

class SomeClass<T : CustomStringConvertible> {
let someStruct: SomeStruct<T>

init(someStruct: SomeStruct<T>) {
self.someStruct = someStruct
}
}


Is there some way to get around the need to re-specify the placeholder type and constraints in the class? Why can't the class just know that
SomeStruct
holds
data: CustomStringConvertible
?

Answer

No, this is not possible. You're trying to describe a concept that would allow specify a concrete (class) type:

class SomeClass { ... }

but using a non-concrete type as a member

class SomeClass { 
    let someStruct: SomeStruct // <-- non-specified generic type, what placeholder should we use?
}

For any concrete types, their members must also be concrete

// OK
class SomeClass { 
    let someStruct: SomeStruct<Int>
}

whereas you could allow generic members if their types is tied to a generic typeholder of the owning type (SomeClass) itself

// OK (assuming there is no type constraint on the
//     generic placeholder in the definition of SomeStruct)
class SomeClass<T> {
    let someStruct: SomeStruct<T>
}

Finally, with regard to the "repeated" type constraints: if the generic placeholder for some given type, say SomeStruct<T> as in your question, is under some type constraint, then naturally (static typing) any typeholder "propagated" as generic type specifier in usages of the generic SomeStruct must be ensured to conform to the same constraints as those applied to the generic placeholder in SomeStruct. Hence, you cannot avoid specifying the same type constraints in the generic class SomeClass<T: ...> to which SomeStruct is used as a generically tied member.

struct SomeStruct<T : CustomStringConvertible> {
    let data: T
}

class SomeClass<T> {
    let someStruct: SomeStruct<T>
                            /* ^ this placeholder is not guaranteed to fulfil
                                 the type constraints which are applied to the
                                 generic typeholder of SomeStruct, hence the
                                 illegality */
}