Is there a way to constrain `Self` to a generic type?
For the generic type Owl<T>
you are allowed to have the constraint where Self: Owl<String>
, but it will only work in contexts where the concrete type information is available.
To make it clear what is happening, consider this:
let nightOwl = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "Owl<String>: Night Owl"
As opposed to this:
let nightOwl: Bird = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "default Bird: Night Owl"
When Swift creates the protocol witness tables for the types Owl<T>
and FlappyBird
, it has to act differently for each one because Owl
is generic. If it doesn't have the concrete type information, i.e. Owl<String>
at the call site, it must use the default implementation for Owl<T>
. This "loss" of type information is happening when you are inserting the owls into the array of type [Bird]
.
In the case of FlappyBird
, since there is only one possible implementation (since it's not generic), the compiler produces a witness table with the "expected" method reference, which is print("FlappyBird: \(name)")
. Since FlappyBird
is not generic, its witness table doesn't need any reference to the unconstrained default implementation of doSomething()
and can therefore correctly call the the constrained implementation even when the concrete type information is missing.
To make it clear that the compiler "needs" to have the fall back behavior for a generic type, you can remove the Bird
conformance from Owl<T>
and try to rely solely on the constrained default implementation. This will result in a compilation error with an error that is, as usual with Swift, highly misleading.
Value of type 'Owl' has no member 'doSomething'
Basically, it seems the witness table can't be built because it requires the existence of an implementation that will work for all types T
on Owl
.
References
- https://forums.swift.org/t/swifts-method-dispatch/7228
- https://developer.apple.com/videos/play/wwdc2016/416/
Unexpected Behavior in Swift Overloaded Function with Generic Parameters or T or Array of T
The compiler is getting confused by the use of the wrapped optional comming out of ary.first which will definitely go to the first function signature. But even fixing that will still leave some ambiguity on the processing of simpler cases such as [[1,2][3,4]].
Another way to approach this would be to handle all the types internally rather than leaving it up to the compiler:
func flatten<U>( _ ary:[Any] ) -> [ U ]
{
func makeFlat(_ flat:[U], _ elem:Any) -> [U]
{
if let item = elem as? U { return flat + [item] }
if let ary = elem as? [U] { return flat + ary }
if let subAry = elem as? [Any] { return flat + flatten(subAry) }
return flat
}
return ary.reduce([U](),makeFlat)
}
Swift Generic constraints in init
The problem is that the first init method
init<T: Equatable>(data: [T])
introduces a local type placeholder T
which hides (and is completely
unrelated to) the placeholder T
of the Generic
type, so it
is essentially the same problem as in Array extension to remove object by value.
As of Swift 2 you can solve that with a "restricted extension":
extension Generic where T : Equatable {
init(data: [T]) {
let handler: (T, T) -> Bool = { $0 == $1 }
compare = handler
// ...
}
}
For Swift 1.x the only solution is probably to define a global helper
function
func makeGeneric<T : Equatable>(data: [T]) -> Generic<T> {
return Generic(compareHandler: { $0 == $1 }, data: data)
}
(and I could not think of a sensible name for the function :).
Related Topics
Write into Settings Bundle in Swift
Custom Uitabbar Unselected Item's Color
Swift Protocol as Generic Parameter
Gcdasyncsocket Multiple Connections Wont Accept Data from Multiple Sockets
Swiftui Multiline Text Background Color
How to Read Id3 Tags/Other Metadata from an Hls Stream in Swift/Avkit
Read Static Property from Object
How to Convert Bytes to Nsstring After Aes Cryptoswift Cipher
Draw a Straight Line in Swift 3 and Core Graphics
Uibutton Font Size Isn't Changing
Use of Nspathcontrol to Represent Virtual Path
How to Pass a Class and Method to Create an Instance of a Class
How to Set iOS 13 Glyphs Programmatically
How to Load Nsview from Xib with Swift 3
Swift 3 Closure Overload Resolution
Fetching Child Sum from Core Data
Change Notification from Observable Object as a Nested Object in Swiftui