Apple Swift: Type Casting Generics
Asked Answered
S

2

6

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:

  1. 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?

Spicule answered 22/6, 2014 at 20:36 Comment(2)
Try using as? but I think in your case its a compiler bug...report it!Claudy
I tried the optional but it didn't work. I agree its probably a compiler bug. It's not the fist one I've seen relating to generics...Spicule
B
1

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!

Birdella answered 22/2, 2015 at 4:8 Comment(0)
E
2

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
Eusporangiate answered 23/6, 2014 at 5:20 Comment(1)
Thank you for the help but I tried your solution to no avail - although your explanation was helpful. I realize that Array<T> cannot always be down cast to Array<Float> or Array<Double> but isn't that what the switch statement is for? It really shouldn't matter... I'm thinking this is an error with the compiler. Also, using the As? operator only generates another error. The Apple documentation (see my link above) does not use the As? operator but rather the As operator only.Spicule
B
1

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!

Birdella answered 22/2, 2015 at 4:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.