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:
.none
(nil
).some(.none)
(a non nilInt???
wrapping a nilInt??
).some(.some(.none))
(a non nilInt???
wrapping a non nilInt??
wrapping a nilInt?
).some(.some(.some(n)))
wheren
is anInt
(anInt
wrapped by 3 layers ofOptional
s)
Similarly, there are three kinds of values that an Int??
can have
.none
.some(.none)
.some(.some(n))
wheren
is anInt
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
Swift. Declaring Private Functions in Internal Protocol
Swift: Can Not Use Array Filter in If Let Statement Condition
Swiftui Pass Two Child Views to View
Animate an Skspritenode with Textures That Have a Size Different from the Original
Coerced to Any' But Property Is of Type Uicolor
Shows the Alert When Uitextfield's Are Full or Empty Swift
Changing Texteditor Background Color in Swiftui for MACos
Navigationview Swiftui Shows Split View in iPad
Comparing Two Enum Variables Regardless of Their Associated Values
Failed to Obtain a Cell from Its Datasource with Swift 3
How to Handle Menu Button Action in Tvos Remote
Pattern Matching in a Swift for Loop
Swiftui: Localizedstringkey with Indices
Play Segment of Avaudiopcmbuffer
Swift: How to Fix Infinite Loop When Adding a Value to a Firebase Variable
Find Out When Uikeyboard.Frame Intersects with Other Frame
Implementing Stringliteralconvertible on Nsurl
Resizing Uimage When Using Sf Symbols - Uiimage(Systemname:)