Deepak Sharma
Deepak Sharma

Reputation: 6581

Swift Expression implicitly coerced from 'Any??' to 'Any?'

I have this code in Swift:

 var dictionary: [String: Any?]? 

 fileprivate func value(forKey key: Key) -> Any? {
    if let dictionary = self.dictionary {
        return dictionary[key.rawValue]
    }

     ...
 }

I get a warning "Expression implicitly coerced from 'Any??' to 'Any?' in the return statement ". What am I doing wrong?

Upvotes: 2

Views: 4144

Answers (4)

Alexander
Alexander

Reputation: 63369

Nobody seemed to actually explain the error message, so I'll go ahead and do that.

Starting with a simple Int

Suppose you had an integer, i, which I'll represent it as this diagram:

let i = 123
// ┏━━━━━━━━━━━━┓
// ┃ Name: i    ┃
// ┃ Type: Int  ┃
// ┠────────────┨
// ┃ Value: 123 ┃
// ┗━━━━━━━━━━━━┛

Any

You can "box up" i into a value of type Any. This container "hides" the Intness of i.

  • This has a benefit of allowing you to handle it alongside other type-erased values, like "s" or true.
  • ...but it has a down side: there really isn't much you can do with a value of type Any. Because Any is so broad, it doesn't really describe much that you can do with the value. You can't .lowercased() it like a String, you can't toggle() it, like a Bool, all you can really do is pass it around, and eventually down-cast it into another type which can do more things.

When you box up i into an Any, it looks something like this:

let any123 = i as Any
// ┏━━━━━━━━━━━━━━━━━━┓
// ┃ Name: any123     ┃
// ┃ Type: Any        ┃
// ┠──────────────────┨
// ┃ Value:           ┃
// ┃  ┏━━━━━━━━━━━━┓  ┃
// ┃  ┃ Name: i    ┃  ┃
// ┃  ┃ Type: Int  ┃  ┃
// ┃  ┠────────────┨  ┃
// ┃  ┃ Value: 123 ┃  ┃
// ┃  ┗━━━━━━━━━━━━┛  ┃
// ┗━━━━━━━━━━━━━━━━━━┛

Any is "opaque" to the type system, as in, the type system cannot determine what lies beyond the Any box border (which is why I represent it with a solid outline).

Optional<T>

Optional is a generic enum with two cases, some (which stores an associated value called wrapped) and none (which doesn't). It's generic over any type T, but for the purposes of this example, I'll talk only about the case where T is Int, so Optional<Int>, a.k.a. Int?.

nil is a shorthand for saying Optional<T>.none, for any type T.

let some123: Int? = 123 // implicitly wraps the value into an optional
let none: Int? = nil

// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓    ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
// ╏ Name: some123    ╏    ╏ Name: none    ╏
// ╏ Type: Int        ╏    ╏ Type: Int?    ╏
// ┠──────────────────┨    ┠───────────────┨
// ╏ Case: .some      ╏    ╏ Case: .none   ╏
// ╏ wrapped:         ╏    ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
// ╏  ┏━━━━━━━━━━━━┓  ╏
// ╏  ┃ Name: i    ┃  ╏
// ╏  ┃ Type: Int  ┃  ╏
// ╏  ┠────────────┨  ╏
// ╏  ┃ Value: 123 ┃  ╏
// ╏  ┗━━━━━━━━━━━━┛  ╏
// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛

Unlike Any, optional is "transparent" to the type system (which is why I represent it with a dashed outline). You can always see the type of the value within.

Composition of Optionals

Optionals can compose with each other, acting like Russian nesting dolls:


let someSome123: Int?? = 123        // Shorthand for Optional<Optional<Int>>.some(Optional<Int>.some(123))
let someOfNone: Int??  = .some(nil) // Shorthand for Optional<Optional<Int>>.some(Optional<Int>.none)
let none: Int??        = nil        // Shorthand for Optional<Optional<Int>>.none

// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓    ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓    ┏╍╍╍╍╍╍╍╍╍╍╍╍╍┓
// ╏ Name: someSome123      ╏    ╏ Name: someOfNone  ╏    ╏ Name: none  ╏
// ╏ Type: Int??            ╏    ╏ Type: Int??       ╏    ╏ Type: Int?? ╏
// ┠────────────────────────┨    ┠───────────────────┨    ┠─────────────┨
// ╏ Case: .some            ╏    ╏ Case: .some       ╏    ╏ Case: .none ╏
// ╏ wrapped:               ╏    ╏ wrapped:          ╏    ┗╍╍╍╍╍╍╍╍╍╍╍╍╍┛
// ╏  ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓  ╏    ╏  ┏╍╍╍╍╍╍╍╍╍╍╍╍╍┓  ╏
// ╏  ╏ Type: Int?       ╏  ╏    ╏  ╏ Type: Int?  ╏  ╏
// ╏  ┠──────────────────┨  ╏    ╏  ┠─────────────┨  ╏
// ╏  ╏ Case: .some      ╏  ╏    ╏  ╏ Case: .none ╏  ╏
// ╏  ╏ wrapped:         ╏  ╏    ╏  ┗╍╍╍╍╍╍╍╍╍╍╍╍╍┛  ╏
// ╏  ╏  ┏━━━━━━━━━━━━┓  ╏  ╏    ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
// ╏  ╏  ┃ Type: Int  ┃  ╏  ╏
// ╏  ╏  ┠────────────┨  ╏  ╏
// ╏  ╏  ┃ Value: 123 ┃  ╏  ╏
// ╏  ╏  ┗━━━━━━━━━━━━┛  ╏  ╏
// ╏  ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛  ╏
// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛   

This happens for example, when you try to subscript a Dictionary that stores T? values. There are 3 possible outcomes:

  1. A value exists for the key, and the value is non-nil. This is like the constant someSome123 above
  2. A value exists for the key, but the value is nil. This is like the constant someOfNone above
  3. No value exists for the key. This is like the constant none above

Many languages allow all types to store a null value. This is a weaker design, because it can't distinguish between #2 and #3. "Does this null mean the value is absent, or that a value is there, but the value itself is null?" The nestability of optionals allows for that distinction . There are many other contexts in which it can come up, like accessing the first value of an Array<Optional<T>>.

Composing Any and Optional

Just like how optionals can contain optionals, optionals can contain values of type Any, and vice versa. Here are some example that recreates your error:

let any123: Any = 123
let optionalOptionalAny123: Any?? = any123
let optionalAny123: Any? = optionalOptionalAny // ⚠️ warning: expression implicitly coerced from 'Any??' to 'Any?'

// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓    ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
// ╏ Name: optionalOptionalAny123  ╏    ╏ Name: optionalAny123             ╏
// ╏ Type: Optional<Optional<Any>> ╏    ╏ Type: Optional<Any>              ╏
// ┠───────────────────────────────┨    ┠──────────────────────────────────┨
// ╏ Case: .some                   ╏    ╏ Case: .some                      ╏
// ╏ wrapped:                      ╏    ╏ wrapped:                         ╏
// ╏   ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓   ╏    ╏  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓  ╏
// ╏   ╏ Type: Optional<Any>   ╏   ╏    ╏  ┃ Type: Any                  ┃  ╏
// ╏   ┠───────────────────────┨   ╏    ╏  ┠────────────────────────────┨  ╏
// ╏   ╏ Case: .some           ╏   ╏    ╏  ┃ Value:                     ┃  ╏
// ╏   ╏ wrapped:              ╏   ╏    ╏  ┃   ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓  ┃  ╏
// ╏   ╏  ┏━━━━━━━━━━━━━━━━━┓  ╏   ╏    ╏  ┃   ╏ Name: some123       ╏  ┃  ╏
// ╏   ╏  ┃ Name: any123    ┃  ╏   ╏    ╏  ┃   ╏ Type: Optional<Int> ╏  ┃  ╏
// ╏   ╏  ┃ Type: Any       ┃  ╏   ╏    ╏  ┃   ┠─────────────────────┨  ┃  ╏   
// ╏   ╏  ┠─────────────────┨  ╏   ╏    ╏  ┃   ╏ Case: .some         ╏  ┃  ╏   
// ╏   ╏  ┃  Value:         ┃  ╏   ╏    ╏  ┃   ╏ wrapped:            ╏  ┃  ╏   
// ╏   ╏  ┃  ┏━━━━━━━━━━━┓  ┃  ╏   ╏    ╏  ┃   ╏    ┏━━━━━━━━━━━┓    ╏  ┃  ╏   
// ╏   ╏  ┃  ┃ i: Int    ┃  ┃  ╏   ╏    ╏  ┃   ╏    ┃ i: Int    ┃    ╏  ┃  ╏   
// ╏   ╏  ┃  ┠───────────┨  ┃  ╏   ╏    ╏  ┃   ╏    ┠───────────┨    ╏  ┃  ╏   
// ╏   ╏  ┃  ┃ Value: 123┃  ┃  ╏   ╏    ╏  ┃   ╏    ┃ Value: 123┃    ╏  ┃  ╏   
// ╏   ╏  ┃  ┗━━━━━━━━━━━┛  ┃  ╏   ╏    ╏  ┃   ╏    ┗━━━━━━━━━━━┛    ╏  ┃  ╏   
// ╏   ╏  ┗━━━━━━━━━━━━━━━━━┛  ╏   ╏    ╏  ┃   ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛  ┃  ╏   
// ╏   ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛   ╏    ╏  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛  ╏   
// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛    ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛   

As you can see, both optionalOptionalAny123 and optionalAny123 contain two layers of optionality, and one layer of type erasure (as provided by Any). This warning exists to tell you that by coersing Any?? to Any?, you're obscuring the second layer of optionality behind the hidden veil of Any.

To get around this warning, you can follow one of its three suggestions:

  1. provide a default value to avoid this warning

    Peel back one layer of optionality by unwrapping its contents, or resorting to the default value if it's nil. This is rarely a good idea, because sensible default values rarely exist.

    let optionalAny123: Any? = optionalOptionalAny ?? 0
    
  2. force-unwrap the value to avoid this warning

    Peel back one layer of optionality by forcefully unwrapping it. This is rarely a good idea, because it requires you be absolutely certain you don't have a nil.

    let optionalAny123: Any? = optionalOptionalAny!
    
  3. explicitly cast to 'Any?' with 'as Any?' to silence this warning

    You can keep the value as-is (with 2 layers of optionality), hiding one of the layers of optionality behind the veil of Any. Adding as Any? doesn't actually do anything at runtime. It's syntactic salt that indicates that you intentionally wanted this behaviour, as opposed to mistakenly letting it slip by.

    let optionalAny123: Any? = optionalOptionalAny as Any?
    

Ideally, you should try to flatten optionality as soon as you can. If the nestedness of optional carries some sort of significance in your system, act upon that as soon as you can, and flatten the optional shortly thereafter.

Upvotes: 7

damdamo
damdamo

Reputation: 353

I want to add an example about what rmaddy said.

Suppose we have the following code:

var dictionary: [String: Any] = ["coco": 1, "foo": "yo"]

func value(dic: [String: Any] , forKey key: String) -> Any {
  // Expression implicitly coerced from 'Any?' to 'Any'
  return dictionary[key]
}

// Optional(1)
print(value(dic: dictionary, forKey: "coco"))

At the beginning, even if we don't declare any optional value, the dictionary don't know if the key really exists. So, you can better understand why you have another optional value that you didn't expect.

Upvotes: 1

Shehata Gamal
Shehata Gamal

Reputation: 100543

Since type of dictionary sub scripting value is Any? with dictionary always append ? to it's key/value access = Any??

you can replace

[String: Any?]? 

with

[String: Any]? 

Upvotes: 3

rmaddy
rmaddy

Reputation: 318924

The value of your dictionary is Any?. The result of accessing a dictionary value is an optional because the key might not exist. So you end up with Any??.

There's really no need for your dictionary to be declared to have optional values.

Change it to [String: Any]? and your issue goes away.

Upvotes: 7

Related Questions