Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apple Swift: Type Casting Generics

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?

like image 223
nalyd88 Avatar asked Jun 22 '14 20:06

nalyd88


People also ask

What are generics Swift?

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.

What is type casting Swift?

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.

How do I call a generic function in Swift?

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.

Why generics are used in Swift?

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.


2 Answers

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
like image 88
Chen Avatar answered Sep 19 '22 03:09

Chen


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!

like image 40
Ray Fix Avatar answered Sep 22 '22 03:09

Ray Fix