Swift Optionals - Why Does Var A:Int? A? = 4 Return Nil

Swift optionals - Why does var a:Int? a? = 4 return nil

The line var a: Int? declares an optional variable with a nil value.

The line a? = 4 makes use of optional chaining to assign a value to the variable a. But if a is nil, the assignment isn't done. And this is your case since a is currently nil. You simply need a = 4 to assign the value of 4 to the variable a.

Swift: About assign nil to a var

Very good question!

Since your array is defined as [String:String] you wonder why the compiler let you assign an optional String (so String?) to a value.

How can the compiler risk you to put a nil inside something that should be a non optional String ?

How can this code compile?

var oldValue: String? = presidentialPetsDict.removeValueForKey("Georgee Bush")
presidentialPetsDict["George W. Bush"] = oldValue

Here's the answer

Subscript has the following logic, even if the value of the Dictionary is String, you can use subscript to assign nil.
In that case the key is removed from the array.

Look here

var numbers: [String:Int] = ["one": 1, "two": 2, "three": 3]
numbers["two"] = nil // it looks like I'm putting nil into a Int right?
numbers // ["one": 1, "three": 3]

Constant unassigned optional will not be nil by default

Yes it's correct

An optional variable doesn't need to be manually initialized. If you read it before having populated it does contain nil.

From Apple docs

If you define an optional variable without providing a default value, the variable is automatically set to nil for you [...]

On the other hand the compiler does force you to manually initialize an Optional constant (let) before you can read it.

Unlike a variable, the value of a constant cannot be changed once it is set. Attempting to do so is reported as an error when your code is compiled [...]

Why?

A constant can be written only once. It doesn't need to happened on the same line it is initialized but it must happened before your read it.

E.g. this code works fine

let num: Int?
num = 1
print(num)

However if the compiler had put a temporary nil value inside num then the constant would have been wrote twice. Which is against the concept of constant.

let num: Int?
print(num) // nil ??? <- this can't work!
num = 1
print(num) // 1

Another example

This code snippet works fine

func printArea(width: Int?, height:Int?) {
let area: Int?
if let width = width, height = height {
area = width * height
} else {
area = nil
}
print(area)
}

Again, if the compiler had put a temporary nil value inside area then...

func printArea(width: Int?, height:Int?) {
let area: Int?
print(area) // not possible! area is going to change in a moment
if let width = width, height = height {
area = width * height
} else {
area = nil
}
print(area)
}

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.

Confused about optional type in swift ( Nil can compare to Int )

(Edit update: after additional question added by OP)

I'll add an answer to you question

"Does it make sense "The nil can compare to Int" in Swift?"

You can consider Optional a type just like Int or double, Double, with a (simplified) enum implementation

enum Optional<T> {
case None
case Some(T)

init(_ value: T) {
self = .Some(value)
}

init() {
self = .None
}
}

The generic T in the Optional type is never used for case .None, which, in this discussion, would be nil. Hence, any type that is also optional (e.g. Int?, String? and so on) can be compared to nil. For the sake of this discussion, you could almost think of nil as a single literal that can be used to initialize or give value to any type that is defined as optional. With this latter statement, it's obvious that we can also compare the values of our optional type to nil.

Hence: yes, we can compare the value of any optional variable to nil, but there are usually better options, e.g. optional chaining or the nil coalescing operator (??).


(From before edit, still relevant)

I'll add an example to help to show you what is going on in loops constructed as if a.b?.c != d { ....

Consider the following structures:

struct MyStruct {
var myRow : Int
init (row: Int) {
myRow = row
}
}

struct MyTopStruct {
var myStruct : MyStruct? = nil

mutating func setValueToMyStruct(row: Int) {
myStruct = MyStruct(row: row)
}
}

Let a be an instance of MyTopStruct, and lets have some integer and optional integer properties in our example:

var a = MyTopStruct()
let myInt = 10
var myOptionalInt : Int? = nil

Then, we have a few different outcomes for if a.b?.c != d { ... clauses, depending on whether optionals in this example are nil (as initialized) or not.

/* "a.myStruct?" returns nil, and this clause will hence
compare "nil != myInt"?, which will always be true, since
myInt is a non-optional integer */
if a.myStruct?.myRow != myInt {
print("Entered 1") // prints
}

/* "a.myStruct?" still nil, and myOptionalInt not yet initialised
so also nil. Hence, "nil != nil"? => false */
if a.myStruct?.myRow != myOptionalInt {
print("Entered 2") // doesnt print
}

/* now initialise a.myStruct */
a.setValueToMyStruct(myInt)

/* "a.myStruct?" now returs value of myInt (10), which != nil,
the latter being the current value of myOptionalInt
=> always enters */
if a.myStruct?.myRow != myOptionalInt {
print("Entered 3") // prints
}

/* finally initialize myOptionalInt */
myOptionalInt = 9

/* Now this is the first comparison that can actually behave
in "different ways" w.r.t. how we usually think of if clauses:
comparing _values_ of same-type objects.
Hence, comparison now depends on the two non-nil values of
a.myStruct?.myRow and myOptionalInt */
if a.myStruct?.myRow != myOptionalInt {
print("Entered 4") // prints, since 10 != 9
}

why multiple optionals(wrap nil) compare with operator == return false?

There are four kinds of values that a Int??? can have:

  1. .none (nil)
  2. .some(.none) (a non nil Int??? wrapping a nil Int??)
  3. .some(.some(.none)) (a non nil Int??? wrapping a non nil Int?? wrapping a nil Int?)
  4. .some(.some(.some(n))) where n is an Int (an Int wrapped by 3 layers of Optionals)

Similarly, there are three kinds of values that an Int?? can have

  1. .none
  2. .some(.none)
  3. .some(.some(n)) where n is an Int

When you write nil, it always means .none of whatever type that context needs, so both opt1 and opt2 are .none here.

What happens when you pass them to the == operator? Well, After some overload resolution/type inference, the compiler finds that == takes a two Int??? parameters, but you have passed an Int?? as the second argument. Luckily, there exists a conversion from any value t of type T to type T? - .some(t).

So after being passed into the ==, opt2 changes from .none to .some(.none), as you can see from this code snippet:

func test(lhs: Int???, rhs: Int???) {
if case .none = lhs, case .some(.none) = rhs {
print("lhs is .none and rhs is .some(.none)")
}
}

test(lhs: opt1, rhs: opt2) // prints

Hence they are "not equal".

The debugger seems to be showing both .none and .some(.none) as "nil", possibly because both of their debugDescription/description is "nil".


If you don't care about the multiple layers of optionals, you can just unwrap them to a single layer by doing as? Int, then compare:

print((opt1 as? Int) == (opt2 as? Int)) // true

Why Wrapped Optional Values in Swift? Is it possible to set a variable as optional without declaring a type?

Is it possible to declare an optional value without assigning a type like String, Int, Bool, etc?

No. The reason is that Optional (without its generic parameter) is not a complete type — think of it as a type constructor, i.e. a way to produce a full type if you give it the missing information.

The compiler needs to know the full type — Optional<Int> or Optional<String> and so on — to allocate the right amount of memory etc. An Optional<String> will take up more memory than an Optional<Bool>, even if both are nil.

The same is true for other generic types like Array. Array is the type constructor, Array<Int> the complete type.

Edit: rmaddy makes a good point: var exampleVar: Any? = nil allows you to store any value in this optional. Note that we're still dealing with a full type here — Optional<Any>. It's just that all types are compatible with Any. In that sense, Optional<Any> is not much different from Optional<SomeProtocol>, which can store any value that conforms to the SomeProtocol protocol.

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