Why Can You Assign Non-Optional Values to Optional Types in Swift

Why can you assign non-optional values to optional types in Swift?

This is part of the syntactic sugar behind optionals. Assigning a non-optional value is how you wrap it in the optional type.

Since an optional indicates the presence or absence of a value, you shouldn't have to do anything special to indicate the presence of a value other than provide one. For example, in a function:

func gimmeSomethingMaybe() -> String? {
if arc4random_uniform(10) > 7 {
return "something"
} else {
return nil
}
}

Imagine if every time you wanted to return a real value from a function that's capable of returning nil, you had to write return Optional(value). That'd get old pretty fast, right? Optionals are an important feature of the language — even though they're actually implemented by the standard library, the syntactic sugar / automatic wrapping is there to keep it from being tedious to use them.


Edit: just to go a bit further into this... the sugar also helps to enforce the notion that a real value should not be optional. For example:

let one = 1
one? // error (in Swift 1.2, allowed but meaningless in Swift 1.1)
"two"? // error (ditto)

You can create an optional wrapping a real value with the Optional(one) initializer, but that has little semantic meaning on its own, so you almost never need to.

Optionals should come into play when there's "mystery" as to whether a value is present or absent — that is, when whether one part of a program receives a value (or no value) depends on state unknown to that part of the program. If you know you have a real value, there's no mystery... instead, you let the unknown come into play at the boundary between the code that knows the value and the code that doesn't know — that is, the function/method/property definition that hands that value off to somewhere.

Swift : If variable a is non-optional then why variable b is an optional?

Variable b is an optional because variable a 'might' return nil and in such case b will also be nil.

Edit:
You have assigned an optional variable to another variable which you have not defined a specific type for. Xcode is saving you by creating the variable b as an optional. Consider this example:

var a: Int! = 6
a = nil
let b: Int = a
print(b)

Similar to yours, variable a here is an optional of type Int and b is a nonoptional of type Int too. Here I specifically define b to be a non optional integer unlike your b variable. Now, if i try to print variable who is assigned value of a which was at that point nil. This is dangerous and simply results in a fatal error at the assignment statement. The crash is simply understood because the compiler finds a nil value in a the optional type and tries to assign it to a non-optional.

Only optionals can hold nil value therefore when you assigned an optional(nillable) to another variable b, it is automatically treated as an optional because the value on which it relies is nillable and hence variable b is also nillable.

Convert an optional Type to non-optional Type

You can use your optional Wrapped type to initialize your nested object:

let obj = MyObj()
let nestedObj = type(of: obj[keyPath: \MyObj.nestedObj]).Wrapped()
nestedObj[keyPath: \NestedObject.nestedProp] = "NEST ME BABY!!"

print(nestedObj.nestedProp ?? "") // "NEST ME BABY!!"

If you want the resulting object to be optional as well:

let obj = MyObj()
var nestedObj = obj[keyPath: \MyObj.nestedObj]
if nestedObj == nil {
nestedObj = type(of: obj[keyPath: \MyObj.nestedObj]).Wrapped()
}
nestedObj?[keyPath: \NestedObject.nestedProp] = "NEST ME BABY!!"

You need to add those helpers:

protocol AnyOptional {
associatedtype Wrapped
var optional: Optional<Wrapped> { get }
}

extension Optional: AnyOptional {
var optional: Optional<Wrapped> { self }
}

Shouldn't an optional be inclusive to a non-optional type in Swift?

The dictionary is not defined to store an optional value. It is just the assignment operator that accepts an optional value because giving it nil will remove the whole key from the dictionary.

The problem you are having is that you are trying to mutate your property items in a non-mutating method. You need to define your method as mutating:

mutating func append(o: T?) {
if let newValue = o {
items[newValue.hashValue] = newValue
}
}

There is certainly no problem assigning an optional variable to a non-optional value:

var optionalString : String? = "Hello, World"

In the same way, it is perfectly valid to assign a dictionary's key to a non-optional value:

var items : [Int:String] = [:]
items[10] = "Hello, World"

You can then assign the key to nil to remove the key entirely from the dictionary:

items[10] = nil

Also, I think you have a fundamental misunderstanding of what a hashValue is and how to use it. You should not pass the value of hashValue to a dictionary as a key. The dictionary calls hashValue on the value you provide, so you are having the dictionary take the hashValue of the hashValue.

There is no guarantee that a hashValue will be different from all other the hashValues of different values. In other words, the hash value of "A" can be the same hash value as "B". The dictionary is sophisticated enough to handle that situation and still give you the right value for a particular key, but your code is not handling that.

Non-optional values in Swift-structure returns optional values


Did I get it wrong?

Yes. Your variables are optionals - the ! operator is a force unwrap operator, so your variables are implicitly unwrapped. If you don't want your variables to be optionals, just drop !.

The ! operator exists in Swift to facilitate bridging from Objective-C, for instance for IBOutlet variables. Using ! operator aside from that is a bad practice.

Why/when are uninitialized non-optional values allowed in Swift for EnvironmentObjects?

The EnvironmentObject property delegate has an init() method taking no parameters, and that provides an implicit initialization for the wrapped properties

@EnvironmentObject var emptyData: EmptyData
@EnvironmentObject var emptyData2: EmptyData

(this is explained in the Modern Swift API Design video roughly at 28:10). So that is why these (non-optional) properties do not need an (explicit) initial value.

The documentation also states that EnvironmentObject is (emphasis added)

... a dynamic view property that uses a bindable object supplied by an ancestor view to invalidate the current view whenever the bindable object changes.

You must set a model object on an ancestor view by calling its environmentObject(_:) method.

So this is how I understand it:

  • If a matching bindable object (in your case: an instance of EmptyData) is found in the environment of the current view or one of its ancestors then the properties are initialized to this object.
  • If no matching bindable object if found in an ancestor view then the program terminates with a runtime error.
  • Environment objects can be used in all, some, or none of the views in the view hierarchy. (See Data Flow Through SwiftUI at 29:20.) Therefore it is not an error to provide an environment object (in your case: an instance of SomeData) which is not used in RandomView.

Using guard with a non-optional value assignment

If you throw a case in there, it'll work. So as follows:

guard case let c = parts.count where c > 1 else { return }

Why non optional Any can hold nil?

TL;DR; Optionals in swift are translated by the compiler to Optional enum instances, and since Any can map to any value, it can be used to store optionals.


How does Swift represent optionals? It does it by mapping SomeType? to a concrete implementation of the Optional enum:

Int? => Optional<Int>
String? => Optional<String>

A simplified declaration of Optional looks like this:

enum Optional<T> {
case none // nil
case some(T) // non-nil
}

Now, a variable of type Any is able to hold an enum value (or any other kind of value, or even metatype information), so it should be able to hold for example a nil String, aka String?.none, aka Optional<String>.none.

Let's see what happens, though. As we see by the Optional declaration, nil corresponds to the .none enum case for all types:

nil == Optional<String>.none // true
nil == Optional<Int>.none // true
[Double]?.none == nil // also true

So theoretically, you should be able to assign nil to a variable declared as Any. Still, the compiler doesn't allow this.

But why doesn't the compiler let you assign nil to an Any variable? It's because it can't infer to which type to map the .none enum case. Optional is a generic enum, thus it needs something to fill the T generic parameter, and plain nil is too broad. Which .none value should it use? The one from Int, the one from String, another one?

This gives an error message supporting the above paragraph:

let nilAny: Any = nil // error: nil cannot initialize specified type 'Any' (aka 'protocol<>')

The following code works, and is equivalent to assigning a nil:

let nilAny: Any = Optional<Int>.none

, as the above Any variable is actually holding a valid value of the Optional enum.

Indirect assignments work too, as behind the scenes nil is converted to Optional<Type>.none.

var nilableBool: Bool? // nilableBool has the Optional<Bool>.none value
var nilBoolAsAny: Any = nilableBool // the compiler has all the needed type information from nilableBool

Unlike other languages, in Swift nil corresponds to a concrete value. But it needs a type to work with, for the compiler to know which Optional<T>.none it should allocate. We can think of the keyword as providing sugar syntax.



Related Topics



Leave a reply



Submit