[Pitch] Nested if let and guard let

Nested if let and guard let


In Swift, the if let statement is commonly used for optional unwrapping. It helps developers write cleaner and safer code by handling optional values. Currently, the if let statement unwraps a single optional value. However, there are cases where we would like to unwrap optional properties of nested objects in a more concise manner. This proposal suggests extending the if let and guard let statements to support nested optional unwrapping.

1. Nested if let

The proposed syntax for nested if let would allow developers to conditionally unwrap optional properties of nested objects. It is as follows:

if let myOptionalObject?.optionalValue {
    // 'optionalValue' is now safely unwrapped and ready to use // *1
} else {
    // Handle the case where 'optionalValue' is nil

2. Nested guard let

Similarly, the proposed syntax for nested guard let would allow developers to conditionally unwrap optional properties of nested objects. It is as follows:

guard let myOptionalObject?.optionalValue else {
    // Handle the case where 'myOptionalObject' or 'optionalValue' is nil
    // This could include returning from the current function, loop, or throwing an error

// 'optionalValue' is now safely unwrapped and ready to use // *1

*1 We can also consider making myOptionalObject safely unwrapped and ready to use.

1 Like

This seems mostly reasonable on the surface. Since SE-0345 landed I remember several cases where I would have like to use this if it were available.

I'm not entirely sure that there's a sufficiently general way to derive a name for the new binding, though. Defining a let optionalValue binding for if let myOptionalObject?.optionalValue could be reasonable, but how could we handle more complex cases like:

if let property.myFunction() { ... }
if let myOptionalObject?.optionalValue.map({ $0.value }) { ... }
if let someArray[0] { ... }
if let myDictionary["dictionaryKey"] { ... }

Would this sort of sugar pull its weight if it only worked for property accesses, and didn't support for these other types of cases?


What’s the advantage over if let obj = myOptionalObject, optionalValue = obj.optionalValue { }? If brevity is the only value, then it comes at the cost of the naming problem @cal mentioned.


moreover, since chaining flattens the optionals, couldn’t this just be

if let optionalValue = myOptionalObject?.optionalValue



@cal great points. It's worth considering the use of shorthand argument names (such as $0) within the if let statement, not sure about guard let.

@ksluder and @taylorswift, the rationale behind this proposal is similar to the introduction of if let optionalValue instead of if let valie = optionalValue. It aims to enhance code readability and conciseness when working with nested optional properties.

By allowing shorthand argument names in if let statements, the unwrapped values become more align with current implementation.

This change would further improve the developer experience.

if let property.myFunction() { /* '$0' is now safely unwrapped and ready to use */ }
if let myOptionalObject?.optionalValue.map { $0.value } { /* '$0' */  }
if let someArray[0] { /* '$0' */ }  
if let myDictionary["dictionaryKey"] { /* '$0' */ }

Brevity is good but not where it creates ambiguity. Ambiguity can be avoided if the rule(s) for the bound name are simple, reasonably intuitive, and consistent. @cal gave some good examples of common cases where it's not apparent to me that there are any rule(s) that can meet that criteria.

Restricting this functionality to just properties (recursively) is likely to be too inconsistent and confusing, I fear. And (secondarily) a hindrance to refactoring (e.g. replacing what was a direct property access with a function call). That said, I'm interested in seeing it explored a bit more.

I recall there was discussion in the context of SE-0345 about allowing this, and it was explicitly rejected. I don't recall if it were for the reasons I just mentioned or some other(s).


This would be breaking working code which simply wants to unwrap a single optional level. Unwrapping a single level of optionality would then require nesting ifs and or guards which would be very annoying for guards. Definitely not a good trade off for sugar IMHO.

at that point why not just write:

property.myFunction().map { // we already have $0 here


myOptionalObject?.optionalValue.map { $0.value }.map { /* $0 */ }

// etc
1 Like

If you really want to shorten if let value = foo?.optionalValue { }, pick a shorter name than value, like x. That avoids the entire problem of automatically picking a name when no good candidates exist.

1 Like

It might be worth noting that Swift has chosen not to use flow analysis to implicitly unwrap optionals, unlike some languages (e.g. Kotlin). So you cannot write e.g.:

if nil != value {

This pitch is, in a way, asking to change that - just with additional syntactic sugar for shortening expression chains so you don't have to be quite so repetitive, a la:

if let something?.somethingMore?.value {

// …is just shorthand for:

if let something?.somethingMore?.value {

There's been quite a few proposals for adopting nilability flow analysis, and they've all been rejected. I don't have an example handy to link to, but it might be insightful to find and review some. I suspect the answer is often "for better or worse Swift chose a different path, and changing it now is too disruptive".

What this pitch brings up as potentially a more interesting argument is the possibility of implicitly unwrapping the whole chain. That actually results in significantly briefer code, e.g.:

if let something?.somethingMore?.value {
    somethingMore.value = nil
    something.somethingElse += 1

// …as an alternative to:

if let something,
   let somethingMore = something.somethingMore,
   let value = somethingMore.value {
    somethingMore.value = nil
    something.somethingElse += 1

An obvious question is how often this actually applies - and relates back to earlier points about how non-property entries in the chain are handled, like method calls; I guess they'd just miss out on this, in which case you have to use the existing, more verbose syntax anyway.

In this example particularly, there's a better way to write it using current Swift syntax.

if let value = something.somethingMore.value {
    something?.somethingMore?.value = nil
    something?.somethingElse += 1

If you really want to shorten something?.somethingMore?.value = nil, you can capture somethingMore as you did, but the pitched change would require pretty much the same code anyway.

In the end, the question is whether saving a small bit of typing is worth dealing with all the cases in which it just can't work.

Given that Swift does not consider cryptic-but-shorter code to be valuable to begin with, I don't see this purely-syntactic change making any headway.

While I agree with your conclusion, this example isn't a great argument for it. I'm assuming you just typo'd the missing ?'s in the if expression, but even beyond that I don't think this is equivalent. Using optional chaining everywhere is not great for readability - it loses something of the author's intent, and makes the code more obtuse - and more error-prone.

e.g. in the above example, it appears that something can be nil inside the if block, but actually it cannot, but actually it could be because something might be a weak reference that nils when doStuff() is called. i.e. this is not actually equivalent to my earlier example, but the fact that it's easily confused with it emphasises why redundant optional chaining is ill-advised.

1 Like

You're correct that I missed the ?s in the if expression. Either way, the pitch doesn't address any of these points.

Same reason for what we have if let optionalValue

Please no. There's a very good reason why we need explicit bindings to variables in Swift, and why shadowing can only happen after an explicit rebinding.

The part that is omitted when

if let foo = foo {

is turned into

if let foo {

is the = foo part, which is not what would happen in case of the magical, hidden binding of

if let myOptionalObject?.optionalValue {
  // the `optionalValue` constant appears out of nowhere

A change like this will produce less clear and more ambiguous code, for the minor gain of saving a few keystrokes, which is not actually a gain because those keystrokes are in fact useful to express the new binding.


I think it's more clear to say that the foo = is being omitted, because if you were omitting the second part that would be omitting the original reference to the variable, since foo is just shadowing the original name.

I don't think that's right, to me a better way to think about it is "= foo is omitted, hence the reference to the original variable, because the original variable has the same name of the new binding".

This is clearly expressed in the proposal (emphasis mine):

If we instead omit the right-hand expression, and allow the compiler to automatically shadow the existing variable with that name, these optional bindings are much less verbose, and noticeably easier to read / write


I agree (so a "-1" from me).

A someone who wrote a lot of Kotlin code and a lot of Swift code I think using nilability flow analysis is a less practical thing, and I like how Swift handles optionals. E.g. see the following Kotlin code with the constant z:

val z: Int? = 1
if (z != null) {
   println(z.dec()) // print z decremented by one

When you change the z into a variable:

var z: Int? = 1
if (z != null) {
   println(z.dec()) // error because z might have changed in the meantime

so you end up with:

var z: Int? = 1
val z2 = z
if (z2 != null) {

which is then exactly what you would always do in Swift (but in Swift in a more elegant way).


Yeah, @ExFalsoQuodlibet's thinking here is supported by the grammar change from SE-0345, which just made = foo (the "initializer") optional:

optional-binding-condition → let pattern initializerₒₚₜ | var pattern initializerₒₚₜ

and also because you are allowed to write explicit type annotations like this:

if let foo: Foo { ... }

which wouldn't be possible if this were instead modeled as omitting the foo =.


I believe allowing what is proposed would give developers an easy path to sacrifice clarity for brevity.

One of the strengths of the original if let shorthand proposal (SE-0345) was that its brevity encouraged clarity by not forcing a redeclaration of an already well-named variable.

With this proposal, with some very common cases, such as the first property of collections, the automatic name would almost never be a clear one, but would definitely be shorter to type:

guard let customers.first else { return }
// 'first' is the easiest name, but almost never a good name
print(first.firstName, first.lastName)

But, since this would be shorter and easier to type, it encourages developers to use poor variable names.

It also sets up the potential of conflicting implied names, for example:

guard let budgeted.total, let actual.total else { return }


guard let locales.first, let languages.first else { return }

Where the shorthand works on the first use but not the second.

The compiler could potentially work around this by creating an automatic variable names such as budgetedTotal, actualTotal, localesFirst, and languagesFirst.

In some cases, that can lead to a descriptive variable name, in some cases like localesFirst, not a great name.

Alternately, there could be a limitation of only one of these allowed per conditional statement:

guard let locales.first, let firstLanguage = languages.first else { return }
// first automatically refers to locales.first
// firstLanguage explicitly refers to languages.first, since the ‘automatic’ name is already taken

In this approach, the rules for which syntax is valid, depending on previous become very complex compared to the current syntax.

Finally, the construct if let locales.first is not declaring something called locales.first, whereas the shorthand introduced with SE-0345, if let foo, is declaring a new variable foo, so stays true to the meaning of let. It's the right hand side of the current expression that is omitted with the shorthand, not the left hand side.

For these reasons, whereas I was strongly for the SE-0345 if let shorthand proposal, I do not support this proposal because I believe what is proposed would lead developers to sacrifice clarity for brevity.