When passing a value into an init function with a generic parameter, you can pass both optional and non-optional values in via the parameter, but inside the function you are unable to differentiate between if you are dealing with an optional or not which can be problematic. I am looking for a way to selectively call one init function if you are passing in an optional and another init function if you are not using an optional. I have managed to solve this actually, however in my solution I get a compiler warning stating "Same-type requirement makes generic parameters 'T' and 'Value' equivalent; this is an error in Swift 6".
Please see code sample and follow up questions below:
import Foundation
struct A<Value> {
init(value: Value) {
print("A init:", value)
}
}
struct B<Value> {
// always called
init(value: Value) {
print("B init for non-optional value:", value)
}
// never will be called
init<T>(value: Optional<T>) where Value == Optional<T> {
print("B init for optional value:", value)
}
}
struct C<Value> {
// called as expected, but compiler gives warning 'Same-type requirement makes generic parameters 'T' and 'Value' equivalent; this is an error in Swift 6'
init<T>(value: T) where Value == T {
print("C init for non-optional value:", value)
}
// called as expected
init<T>(value: Optional<T>) where Value == Optional<T> {
print("C init for optional value:", value)
}
}
let nonOptionalString: String = "1"
let optionalString: String? = nil
_ = A(value: nonOptionalString) // prints 'A init: 1'
_ = A(value: optionalString) // prints 'A init: nil'
_ = B(value: nonOptionalString) // prints 'B init for non-optional value: 1'
_ = B(value: optionalString) // prints 'B init for non-optional value: nil' <-- undesired behaviour
_ = C(value: nonOptionalString) // prints 'C init for non-optional value: 1'
_ = C(value: optionalString) // prints 'C init for optional value: nil' <-- desired behaviour
Struct A demonstrates base case behaviour of one init method that you can pass both optional and non-optional values into. Within the init you are unable to treat the value as an optional even if an optional was passed in (this makes sense of course, but means you can never deal with the optionality of a passed in optional).
Struct B demonstrates adding an additional init method to handle the optional. This gives no compiler warning, but this additional init method never gets called.
Struct C demonstrates a working solution similar to struct B but where the the original init method is modified to use function generic type T and constrain T equal to Value. This solution works but gives a compiler warning related to upcoming changes in Swift 6.
Questions:
- Is there a better way to accomplish calling different init methods for optional vs non-optional values passed into a generic parameter?
- Why do the init methods in struct B and struct C differ in their behaviour? The compiler is correct when it says T and Value are equivalent, but T constrained equal to Value is actually treated differently than just using Value directly.
Thanks for your help!