Treating a forced downcast as optional will never produce 'nil'
Treating a forced downcast as optional will never produce 'nil'
I've been playing around with Swift and discovered that when down casting an object to be inserted into a dictionary, I get a weird warning: Treating a forced downcast to 'String' as optional will never produce 'nil'
. If I replace as
with as?
then the warning goes away.
Treating a forced downcast to 'String' as optional will never produce 'nil'
as
as?
func test() -> AnyObject!
return "Hi!"
var dict = Dictionary<String,String>()
dict["test"]=test() as String
Apple's documentation says the following
Because downcasting can fail, the type cast operator comes in two different forms. The optional form, as?
, returns an optional value of the type you are trying to downcast to. The forced form, as
, attempts the downcast and force-unwraps the result as a single compound action.
as?
as
I'm unclear as to why using as?
instead of as
is correct here. Some testing reveals that if I change test() to return an Int instead of a String, the code will quit with an error if I continue using as
. If I switch to using as?
then the code will continue execution normally and skip that statement (dict will remain empty). However, I'm not sure why this is preferable. In my opinion, I would rather the program quit with an error and let me know that the cast was unsuccessful then simply ignore the erroneous statement and keep executing.
as?
as
as
as?
According to the documentation, I should use the forced form "only when you are sure that the downcast will always succeed." In this case I am sure that the downcast will always succeed since I know test()
can only return a String so I would assume this is a perfect situation for the forced form of down casting. So why is the compiler giving me a warning?
test()
import Foundation
String
AnyObject
String
NSString
String
as
@newacct Interestingly enough, if you replace String with NSString in either the Dictionary definition or the downcast, the warning goes away. If you replace String with NSString in both places, however, the warning remains.
– Pamelloes
Jun 14 '14 at 20:59
NOTE: Since Swift 2, many of the functionalities of
as
have been removed, and been replaced with as!
. So you you will get the same error when you use as!
(using as
will not generate this error anymore)– Honey
Oct 20 '17 at 12:49
as
as!
as!
as
2 Answers
2
Let's take a closer look at your last line, and explode it to see what's happening:
let temporaryAnyObject = test()
let temporaryString = temporaryAnyObject as String
dict["test"] = temporaryString
The error is on the second line, where you are telling the compiler to enforce that temporaryAnyObject
is definitely a String
. The code will still compile (assuming you don't treat warnings as errors), but will crash if temporaryAnyObject
is not actually a String
.
temporaryAnyObject
String
temporaryAnyObject
String
The way as
works, with no ?
, is basically saying "from now on, treat the result of this expression as that type IF the result is actually of that type, otherwise we've become inconsistent and can no longer run.
as
?
The way as?
works (with the ?
) is saying "from now on, treat the result of this expression as that type IF the result is actually of that type, otherwise the result of this expression is nil
.
as?
?
nil
So in my exploded example above, if test()
does return a String
, then the as
downcast succeeds, and temporaryString
is now a String
. If test()
doesn't return a String
, but say an Int
or anything else not subclassed from String, then the as
fails and the code can no longer continue to run.
test()
String
as
temporaryString
String
test()
String
Int
as
This is because, as the developer in complete control, you told the system to behave this way by not putting the optional ?
indicator. The as
command specifically means that you do not tolerate optional behavior and you require that downcast to work.
?
as
If you had put the ?
, then temporaryString
would be nil
, and the third line would simple remove the "test" key/value pair from the dictionary.
?
temporaryString
nil
This might seem strange, but that's only because this is the opposite default behavior of many languages, like Obj-C, which treat everything as optional by default, and rely on you to place your own checks and asserts.
Edit - Swift 2 Update
Since Swift 2, the forced, failable downcast operator as
has been removed, and is replaced with as!
, which is much Swiftier. The behavior is the same.
as
as!
I understand what you're saying about "as" vs "as?" but I still don't get why the warning appears. Furthermore, if I split my code into three lines like you outlined the warning actually disappears. It only shows up when the statement occurs in one line.
– Pamelloes
Jun 14 '14 at 21:05
Ah, you're right. Well, that's actually because I wasn't incorrect in the explosion. This is actually what it would be:
let temporaryAnyObject: AnyObject = test(); let temporaryString: String? = temporaryAnyObject as String; dict["test"] = temporaryString
and as you'll see, that gives the same warning.– Ryan
Jun 14 '14 at 23:11
let temporaryAnyObject: AnyObject = test(); let temporaryString: String? = temporaryAnyObject as String; dict["test"] = temporaryString
And the reason why is because
dict["test"] = *
is expecting a String?, so the change I just posted explicitly types as a String?, not a String!. Think about the "as" as a function. The "as" function returns a String! whereas "as?" returns as String?– Ryan
Jun 14 '14 at 23:13
dict["test"] = *
This stems from the fact that you can remove a key (let's say "apples") by calling dict["apples"] = nil.
– Ryan
Jun 15 '14 at 15:58
With yesterday's update to the Swift compiler, the warning now offers a solution of putting the downcast in parentheses instead of replacing it with an optional downcast.
– Pamelloes
Jun 18 '14 at 19:08
You can solve this warning from two angles. 1. The value you return 2. The type that you are expected to return. The other answer speaks about the 1st angle. I'm speaking about the 2nd angle
This is because you are returning a forced unwrapped and casted value for an optional. The compiler is like, "If you really want to just force cast all optionals then why not just make the expected return parameter to be a non-optional"
For example if you wrote
func returnSomething<T> -> T? // I'm an optional, I can handle nils SAFELY and won't crash.
return UIViewController as! T // will never return a safe nil, will just CRASH
Basically you told yourself (and the compiler) I want to handle nil
s safely but then in the very next line, you said nah, I don't!!!
nil
The compiler would give a warning:
Treating a forced downcast to 'T' as optional will never produce 'nil'
An alternative is to remove the ?
?
func returnSomething<T>() -> T // I can't handle nils
return UIViewController() as! T // I will crash on nils
Having that said, likely the best way is to not use force cast and just do:
func returnSomething<T>() -> T? // I can handle nils
return UIViewController() as? T // I won't crash on nils
Not force casting worked for me (e.g.
as?
), thanks!– Andi
May 7 at 13:24
as?
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
First of all, there's a lot of Cocoa type bridging going on, because without
import Foundation
, it wouldn't even compile, becauseString
is not a class type and is not compatible withAnyObject
. If you change the lastString
toNSString
, the warning goes away, meaning probably that it is an issue with you usingString
, a non-class type, in theas
.– newacct
Jun 14 '14 at 20:40