I'm writing some Swift code where I have an array containing a generic type:
let _data: Array<T> = T[]()
Later in my code I need to determine the type stored in the array. I tried using the type casting technique described in the documentation (although it was not used for generics).
switch self._data {
case let doubleData as Array<Double>:
// Do something with doubleData
case let floatData as Array<Float>:
// Do something with floatData
default:
return nil // If the data type is unknown return nil
}
The above switch statement results in the following error upon compilation:
- While emitting IR SIL function @_TFC19Adder_Example___Mac6Matrix9transposeUS_7Element__fGS0_Q__FT_GSqGS0_Q___ for 'transpose' at /code.viperscience/Adder/src/Adder Library/Matrix.swift:45:3 :0: error: unable to execute command: Segmentation fault: 11 :0: error: swift frontend command failed due to signal (use -v to see invocation) Command /Applications/Xcode6-Beta2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift failed with exit code 254
Anybody know how I can cast my generic data to its actual type in order to take specific action?
Swift Generics allows us to create a single function and class (or any other types) that can be used with different data types. This helps us to reuse our code.
Type casting in Swift is implemented with the is and as operators. These two operators provide a simple and expressive way to check the type of a value or cast a value to a different type. You can also use type casting to check whether a type conforms to a protocol, as described in Checking for Protocol Conformance.
Solution. A generic function that you might need to use explicit specialization is the one that infer its type from return type—the workaround for this by adding a parameter of type T as a way to inject type explicitly. In other words, we make it infer from method's parameters instead.
Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner.
In swift, as
operator is something like dynamic_cast
in C++, which can be used to down cast an object.
Say you have an object a
of type A
, and you can write let a as B
only when type B
is identical to type A
, or B
is a sub-class of A
.
In your case, apparently Array<T>
cannot always be down cast to Array<Double>
or Array<Float>
, so compiler reports errors.
A simple fix is to convert to AnyObject
first, and then downcast to Array<Double>
or Array<Float>
:
let anyData: AnyObject = self._data;
switch anyData {
case let doubleData as? Array<Double>: // use as? operator, instead of as,
// to avoid runtime exception
// Do something with doubleData
case let floatData as? Array<Float>:
// Do something with floatData
default:
return nil // If the data type is unknown return nil
Suppose you have an array of buttons:
let views: [NSView] = [NSButton(), NSButton(), NSButton()]
You can use these casts:
let viewsAreButtons = views is [NSButton] // returns true
let buttonsForSure = views as! [NSButton] // crashes if you are wrong
let buttonsMaybe = views as? [NSButton] // optionally set
If you try to use as in a switch case like below, it will not work. The compiler (Swift 1.2 Xcode 6.3b1) says: "Downcast pattern of type [NSButton] cannot be used."
switch views {
case let buttons as [NSButton]:
println("Buttons")
default:
println("something else")
}
Call it a limitation. File a radar with your use case. The Swift team really seams to be listening for feedback. If you really want to get it to work, you can define your own pattern matching operator. In this case it would be something like this:
struct ButtonArray { }
let isButtonArray = ButtonArray()
func ~=(pattern: ButtonArray, value: [NSView]) -> Bool {
return value is [NSButton]
}
Then this works:
switch views {
case isButtonArray:
println("Buttons") // This gets printed.
default:
println("something else")
}
Try it in a Playground. Hope it helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With