By Keiwan


2016-09-16 17:32:00 8 Comments

Why are implicitly unwrapped optionals not unwrapped when using string interpolation in Swift 3?

Example: Running the following code in the playground

var str: String!
str = "Hello"

print("The following should not be printed as an optional: \(str)")

produces this output:

The following should not be printed as an optional: Optional("Hello")

Of course I can concatenate strings with the + operator but I'm using string interpolation pretty much everywhere in my app which now doesn't work anymore due to this (bug?).

Is this even a bug or did they intentionally change this behaviour with Swift 3?

1 comments

@Hamish 2016-09-16 17:59:37

As per SE-0054, ImplicitlyUnwrappedOptional<T> is no longer a distinct type; there is only Optional<T> now.

Declarations are still allowed to be annotated as implicitly unwrapped optionals T!, but doing so just adds a hidden attribute to inform the compiler that their value may be force unwrapped in contexts that demand their unwrapped type T; their actual type is now T?.

So you can think of this declaration:

var str: String!

as actually looking like this:

@_implicitlyUnwrapped // this attribute name is fictitious 
var str: String?

Only the compiler sees this @_implicitlyUnwrapped attribute, but what it allows for is the implicit unwrapping of str's value in contexts that demand a String (its unwrapped type):

// `str` cannot be type-checked as a strong optional, so the compiler will
// implicitly force unwrap it (causing a crash in this case)
let x: String = str

// We're accessing a member on the unwrapped type of `str`, so it'll also be
// implicitly force unwrapped here
print(str.count)

But in all other cases where str can be type-checked as a strong optional, it will be:

// `x` is inferred to be a `String?` (because we really are assigning a `String?`)
let x = str 

let y: Any = str // `str` is implicitly coerced from `String?` to `Any`

print(str) // Same as the previous example, as `print` takes an `Any` parameter.

And the compiler will always prefer treating it as such over force unwrapping.

As the proposal says (emphasis mine):

If the expression can be explicitly type checked with a strong optional type, it will be. However, the type checker will fall back to forcing the optional if necessary. The effect of this behavior is that the result of any expression that refers to a value declared as T! will either have type T or type T?.

When it comes to string interpolation, under the hood the compiler uses this initialiser from the _ExpressibleByStringInterpolation protocol in order to evaluate a string interpolation segment:

/// Creates an instance containing the appropriate representation for the
/// given value.
///
/// Do not call this initializer directly. It is used by the compiler for
/// each string interpolation segment when you use string interpolation. For
/// example:
///
///     let s = "\(5) x \(2) = \(5 * 2)"
///     print(s)
///     // Prints "5 x 2 = 10"
///
/// This initializer is called five times when processing the string literal
/// in the example above; once each for the following: the integer `5`, the
/// string `" x "`, the integer `2`, the string `" = "`, and the result of
/// the expression `5 * 2`.
///
/// - Parameter expr: The expression to represent.
init<T>(stringInterpolationSegment expr: T)

Therefore when implicitly called by your code:

var str: String!
str = "Hello"

print("The following should not be printed as an optional: \(str)")

As str's actual type is String?, by default that's what the compiler will infer the generic placeholder T to be. Therefore the value of str won't be force unwrapped, and you'll end up seeing the description for an optional.

If you wish for an IUO to be force unwrapped when used in string interpolation, you can simply use the force unwrap operator !:

var str: String!
str = "Hello"

print("The following should not be printed as an optional: \(str!)")

or you can coerce to its non-optional type (in this case String) in order to force the compiler to implicitly force unwrap it for you:

print("The following should not be printed as an optional: \(str as String)")

both of which, of course, will crash if str is nil.

@Andreas 2016-10-30 21:08:51

As if it weren't bad enough that you have to use an IUO, now they're less useful and behave contradictorily. If I use one it's because I want it to be implicitly unwrapped. They should be called Schrödinger Optionals from now on.

@Alexander 2016-11-30 20:00:20

Just don't use them. 👍🏻

@Joris Mans 2016-12-17 14:24:33

The explanation given is great. The fact that this explanation needs to be given is not. The more I work with Swift, the more I am under the impression that they simply replaced one type of complexity from objc with another type, and that in the end, it's just as hard/wasy to use one or the other. How this kind of stuff should make programmer's lives easier is beyond me.

@la_f0ka 2017-03-09 09:57:00

Between this kind of stuff and the amount of love they give to Swift support in XCode its hard to believe that this is the language Apple is pushing us to use

@drew.. 2017-09-08 17:20:16

While i am unexpectedly loving Swift as compared to Obj-C, i must admit that seemingly 75% of my coding efforts directly or indirectly involve correcting optional variable requirements. It is a stunning amount of effort for what is a simple enough issue: is an object nil or not? Surely there must have been better options (pun intended).

@Robert Dundon 2018-03-06 18:29:59

Coming from a JS/PHP background, I never realized how spoiled I was dealing with variable types :O Of course those languages have their own problems ;)

@crypt 2018-07-09 11:14:53

But why does the implicit unwrapping has stopped only in case of String data types? In my dictionary, if I have 2 parameters: one Int! type and other String! type, the Int! type is unwrapped but String! type comes as some("myValue").

@Hamish 2018-07-14 16:07:29

@crypt They should behave consistently AFAIK – could you please provide a bit more context about your dictionary and how you're adding the IUO values to it? Note that you'll get different results from using the value in a literal vs. assigning through the dictionary subscript, compare stackoverflow.com/q/50051964/2976878

@crypt 2018-07-20 11:33:20

@Hamish Yes you are right. There is no inconsistency. It works as you have described. Thanks.

Related Questions

Sponsored Content

22 Answered Questions

[SOLVED] Convert Int to String in Swift

9 Answered Questions

33 Answered Questions

[SOLVED] Split a String into an array in Swift?

17 Answered Questions

[SOLVED] How can I do string interpolation in JavaScript?

9 Answered Questions

[SOLVED] Swift Beta performance: sorting arrays

0 Answered Questions

Implicitly unwrapped optional in Swift

  • 2016-12-14 09:36:23
  • 홍한석
  • 47 View
  • 1 Score
  • 0 Answer
  • Tags:   ios swift

1 Answered Questions

[SOLVED] Why should I unwrap implicitly unwrapped optional?

  • 2016-11-30 19:40:13
  • Leo
  • 130 View
  • 2 Score
  • 1 Answer
  • Tags:   swift

2 Answered Questions

[SOLVED] No need to unwrap optionals in Swift?

  • 2014-06-04 14:34:23
  • wavers
  • 3267 View
  • 3 Score
  • 2 Answer
  • Tags:   ios swift

2 Answered Questions

[SOLVED] If let var - Unwrapping optional value

  • 2015-06-05 13:14:49
  • Toldy
  • 2835 View
  • 7 Score
  • 2 Answer
  • Tags:   ios swift

1 Answered Questions

[SOLVED] Implicitly Unwrapped Optionals and println

Sponsored Content