Casting from Any to Optional

Currently (in Swift 4.2.1), it is an error to cast a value from Any to Optional, even when that value was created as the exact same Optional type and previously cast to Any. Here is an example:

let x: Int? = 1
let y: Any = x as Any
print(type(of: y))          // "Optional<Int>\n"

let z: Int? = y as! Int?    // error: cannot downcast from 'Any' to a more optional type 'Int?'

Is this a bug, or an oversight, or an intentional design decision? In particular, would it require an evolution proposal to make it possible for this to work? My expectation was that Any could wrap any type, and be unwrapped back to that type.

So far the only way I have found to extract the original Optional value, is via a generic helper function such as:

func unwrap<T>(_ x: Any) -> T {
  return x as! T
}

let z: Int? = unwrap(y)
print(type(of: z))          // "Optional<Int>\n"

It seems rather peculiar that we can attempt to unwrap an Any into an unspecified generic type, but we are prohibited from unwrapping an Any into the exact specific type we know it contains, if that type is Optional.

2 Likes

I agree that this feels like an arbitrary limitation, especially given that this works exactly as expected:

let maybeInt = 1 as Int?
let anything = maybeInt as Any

let opened = anything as? Int?
// error: cannot downcast from 'Any' to a more optional type 'Int?'

func dynamicCast<T>(_ value: Any, to _: T.Type) -> T? {
  if let value = value as? T {
    return value
  } else {
    return nil
  }
}

if let opened = dynamicCast(anything, to: Int?.self) {
  print("got an optional Int: \(opened)")
} else {
  print("it's something else entirely")
}

I think we added this to stop people from expecting x as! Int? to act like x as? Int. It's not really a correct error when casting from any (non-class) existential / protocol value (Any is "no protocols").

Could you elaborate? I run into the error described here multiple times, but I've never tried to do x as! Int? instead of x as? Int. Why would someone think that they're the same?

:-/ I don't have much here, but I remember it coming up in the first few years of Swift. It's also logic shared with x as? Int?, which is a more reasonable typo to make (because you want the result type to be optional). The compiler could certainly make a different decision about diagnosis based on as? vs. as!.

Terms of Service

Privacy Policy

Cookie Policy