4

Is it possible to check a value confirms a generic protocol dynamically?

I would like to do something like this:

import func Darwin.atoll
func anyToInt(a: Any) -> IntMax {
    if let v = a as? IntegerType { // error!!
        return v.toIntMax()

    } else {
        return atoll("\(a)")
    }
}

This makes compile errors with a message "error: protocol 'IntegerType' can only be used as a generic constraint ...".

If I used a correct static type, I would use overloading by type-parameter constraints:

func anyToInt<T where T: IntegerType>(a: T) -> IntMax {
    return a.toIntMax()
}
func anyToInt<T>(a: T) -> IntMax {
    return atoll("\(a)")
}

Unfortunately, however, there's no method to employ static types instead of Any in my case.

FUJI Goro
  • 869
  • 10
  • 19
  • what happens if u give let v = a as? Int instead of what you have given? – rakeshbs Jan 02 '15 at 05:45
  • http://stackoverflow.com/questions/24299635/downcast-from-any-to-a-protocol – bluedome Jan 02 '15 at 05:47
  • There are a lot of types that confirm IntegerType: Int, UInt, Int8, UInt8, Int16, UInt16, and etc. Thus, I want to handle them as a single IntegerType. – FUJI Goro Jan 02 '15 at 08:11
  • Swift intentionally does not let you treat all integer types interchangeably. Its casting requirement isn't something to work around. Ignoring how the various ranges interact is a historic cause of bugs, and Swift wanted to avoid that. You should build your code to work with `Int` unless you have some specific reason to work with another integer type. See "Numeric Type Conversion" https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_467 Working with other integer types should be explicit. – Rob Napier Jan 02 '15 at 14:59

1 Answers1

2

You can’t, for a couple of reasons:

Firstly, you can’t cast the contents of an Any to a protocol that the Any might support. You can only get out the explicit type that’s contained:

let i = 1
i as Printable  // works

let a: Any = i
a as? Printable  //  doesn’t work
let p = a as Int as Printable // works
// or, if you don’t want to crash if a isn’t an Int
let p = (a as? Int).map { $0 as Printable } 
// (is there a cleaner syntax than this?)

Secondly, you can’t instantiate protocols that have an associated type. You can only use them as generic constraints. That example with Printable only works because it doesn’t have an associated type. See this answer for an explanation of why. So even if casting Any could work on protocols, you couldn’t cast it to an IntegerType.

But are you certain you have to use Any here? Any really is best avoided unless there’s no alternative. Maybe you can push generics further up the pipeline?

Community
  • 1
  • 1
Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • Thanks to the answer. In fact, I'd like to use variable-length arguments like `sprintf(format: String, _ args: Any...)` because Swift doesn't support variadic generics :( – FUJI Goro Jan 02 '15 at 23:49