The difference between Dog and Dog.self is that "Dog" is just the name of the type, but "Dog.self" is a reference to the metatype object that represents the type. As it happens, it's possible for the compiler to distinguish between places where the name is required, and places where a metatype object is required, and it's a planned future enhancement to Swift to allow them both to be written Dog — but it's likely not a small change internally.
For now, you could try using any as! Dog? to "safely" cast an unknown optional to an optional Dog. It perhaps doesn't do exactly the same thing as your "cast" function, but it should be close enough. (For example, the two techniques might produce different results for a value of type Dog?? — that's a double optional, which is an edge case you probably don't care about.)
> any as! Dog?
wow, looks strange, but does the trick indeed!
out of curiousity, how do i create "Dog??" or "Dog???"
The most plausible scenario of intentionally creating a multilevel optional is from a dictionary with optional values:
let dict: [String: Dog?] = ... some values ... let value = dict ["fido"]
Looking up a value in a dictionary adds a level of optionality, so "value" would be of type Dog?? FWIW, compare the meaning of the following two lines:
dict ["fifi"] = nil as Dog? dict ["mimi"] = nil as Dog??
The first of those puts a nil value into the dictionary under key "fifi". The second deletes the value under key "mimi".
More abstractly, you can add as many levels of optionality as you want, if you remember that Optional is an enum. You can write things like:
let weirdo = Optional.some(Optional.some(Optional.some(rex)))
"weirdo" is of type Dog???
interesting.. and this:
dict["zizi"] = nil
acts here the same way as "dict["zizi"] = nil as Dog??" deleting the key.
are these two equivalent?
any as? Dog
any as? Dog?
and these two:
any as! Dog
any! as! Dog
dict["zizi"] = nil
deletes the entry, because the type of "nil" is inferred to be "Dog??".
Your other pairs of example are more or less equivalent, but again I'm not sure they're exactly the same in edge cases. What's going on behaind the scenes is that the compiler has some ad-hoc rules about when it can ignore an extra level of optionality in the value being tested.
A related example is an optional try:
let result = try? someDog(named: "spot")
Previously, this would have added a level of optionality to the result. If "someDog" is defined to return a Dog?, the "try" would produce a Dog??. In Swift 5, this was changed to produce only a Dog?, eliminating one of the two levels of optionality.
The downside of this sort of thing is that it's harder to think about what's happening in terms of a simple rule. The upside is that the behavior is much more usable in typical cases.
> The downside of this sort of thing is that it's harder to think about what's happening in terms of a simple rule.
this is a bit worrisome. as your example with mimi vs fifi shows sometimes the number of ??? is important, and this fuzzy logic of compiler collapsing levels of optionality "with good intentions" might actually break something.