# \[Pitch\] Last expression as return value

**URL:** <https://forums.swift.org/t/pitch-last-expression-as-return-value/76958>\
**Category:** Pitches\
**Created:** [January 2, 2025, 12:37am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958 "2025-01-02T00:37:10Z")\
**Posts on this page:** 20\
**Page:** 17

<div class="post-metadata">

**Author:** ![sspringer](https://avatars.discourse-cdn.com/v4/letter/s/e9c0ed/32.png) [@sspringer](https://forums.swift.org/u/sspringer)\
**Post date:** [January 11, 2025, 7:13pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/339 "2025-01-11T19:13:35Z")

</div>

> [@austintatious](#):
>
> What if it were allowed in multi-lined closures/functions and in the course of development I comment out my return statement and now a unsuspecting discardableResult becomes the return value?

Discardable results are a strange thing, maybe a warning can be added if it determines the type of a closure (@ben-cohen)? (You then would have to add “return ” or “\_ = “ to silence that warning.) Just as with autoclosures, discardable results should maybe only be taught later in an introductory course, at least if there is no such warning. The promise with Swift was never to be easy, but to follow the principle of progressive disclosure.

However, experiences with teaching beginners are valuable as a feedback.

---

<div class="post-metadata">

**Author:** ![SlugFiller](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/slugfiller/32/33973_2.png) [@SlugFiller](https://forums.swift.org/u/SlugFiller)\
**Post date:** [January 11, 2025, 7:57pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/340 "2025-01-11T19:57:00Z")

</div>

> [@ellie20](#):
>
> What I mean is that, for example,
> 
> ```swift
> let x = expression()
> 
> ```
> 
> would need to be refactored into
> 
> ```swift
> let x = do {
> sideEffect()
> then expression()
> }
> 
> ```

I get the sentiment. But this example does not work. The proper refactoring for this would be:

```swift
sideEffect()
let x = expression()

```

You only need to add `sideEffect` inside the same block as `expression`, if `expression` is already inside an expression-like `if` or `switch`, i.e. it only happens in one possible branch of the code. The other possible case would be if `expression` is in a ternary:

```swift
let x = condition ? expression() : value

```

But then to add something that happens before `expression`, you need to refactor the whole thing into an `if`, because you can neither multi-line nor `then` \[1\] inside a ternary.

At the same time, the ternary example makes for an interesting question: Are expression `if`/`switch` supposed to be more powerful than a ternary? Is this an intended purpose for them, to have ternary be explicitly weaker? Or was the original purpose to allow long cascaded ternaries like `a >= b ? a > b ? 1 : 0 : -1` to be replaced with something more readable?

Because no one would be wondering "Why can't I add a `print("oh oh")` to one branch of a ternary". Or, if they did, it would involve a very different design.

* * *

1. I really prefer `break`, leaning towards `break = value` at the moment. I think one of the biggest objections to the `then` proposal was the introduction of a new keyword, not the actual usage thereof, but I didn't read the whole thread.

---

<div class="post-metadata">

**Author:** ![Torust](https://avatars.discourse-cdn.com/v4/letter/t/f17d59/32.png) [@Torust](https://forums.swift.org/u/Torust)\
**Post date:** [January 11, 2025, 9:28pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/341 "2025-01-11T21:28:19Z")

</div>

Like a few other people, my reaction to this pitch is “I don’t like it”, with a hard time articulating exactly why. I _think_ my core objection is that when reading code, I like there being some visual marker to say "this is where the return value is; this is what you need to pay attention to". `return` keywords explicitly provide that; in a single line closure I think it's obvious from context, as it is for `if` or `switch` expressions with single line returns; and in a language like Rust it's (subtly) denoted by the lack of a `;`. What's proposed here is using only the _position_ of a line of code amongst potentially many, and to me that just doesn't indicate that something is a return value.

To propose an alternative: I think my preferred solution to that would be to burden the lines that _aren't_ the return value, similar to what Rust does with the semicolon. In Swift, that would mean that if we say everything in a `= switch`, `= if`, or (maybe) `= do` is an expression, you'd explicitly mark the lines which you don't want to return the result for with an `_ = `. To pull a couple of examples from this thread:

```swift
let width = switch scalar.value {
    case 0..<0x80: 1
    case 0x80..<0x0800: 2
    case 0x0800..<0x1_0000: 3
    default: 
      _ = log("this is unexpected, investigate this") // otherwise you'd get an error that the line is returning 'Void'
     4
}

```

```swift
let carBackDoorLabel = switch carBackDoorButtonAction {
  case .lock: 
    "Locked" // returns
    print("childSafety: \(childSafety)") // error: wrong return type, unreachable code
    "Locked?" // warning: unreachable code
  case .unlock: 
    _ = print("childSafety: \(childSafety)") // prints
    if childSafety { 
      "Locked" // returns
    } else {
      "Unlocked" // returns
    }
    print("childSafety: \(childSafety)") // error: wrong return type, unreachable code
}

```

In other words, the rule would be that in expression contexts, anything that is an r-value (i.e. not a `_ = `, `let _ = `, or `func` declaration) is returned as a result of the expression. To my taste, those `_ =`s give enough of a visual indicator that "something is different in this context; the unmarked lines must be expression return values" in a way that the bare last expression just doesn't.

---

<div class="post-metadata">

**Author:** ![Nobody1707](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nobody1707/32/2169_2.png) [@Nobody1707](https://forums.swift.org/u/Nobody1707)\
**Post date:** [January 11, 2025, 11:38pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/342 "2025-01-11T23:38:50Z")

</div>

I believe one of the motivations for `if` expressions was to discourage the use of ternaries as much as possible. Not merely to improve readability, but to reduce the amount of expensive bidirectional type checking.

---

<div class="post-metadata">

**Author:** ![georgeef](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/georgeef/32/38378_2.png) [@georgeef](https://forums.swift.org/u/georgeef)\
**Post date:** [January 11, 2025, 11:51pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/343 "2025-01-11T23:51:57Z")

</div>

> [@ben-cohen](#):
>
> For closures, the last expression will be used to infer the type of the outer closure.

The inferred type of

```swift
let x = {
  // some code without return
  return optional?.voidMethod()
}()

```

depends on the presence or absence of `return` (`'()?'` or `'()'`, resp.). What is the type with the last expression rule, if `return` is removed?

---

<div class="post-metadata">

**Author:** ![SlugFiller](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/slugfiller/32/33973_2.png) [@SlugFiller](https://forums.swift.org/u/SlugFiller)\
**Post date:** [January 11, 2025, 11:55pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/344 "2025-01-11T23:55:56Z")

</div>

> [@Nobody1707](#):
>
> reduce the amount of expensive bidirectional type checking

Isn't the bidirectional type checking necessary for `if` expressions as well?

---

<div class="post-metadata">

**Author:** ![ibex](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ibex/32/30439_2.png) [@ibex](https://forums.swift.org/u/ibex)\
**Post date:** [January 12, 2025, 12:06am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/345 "2025-01-12T00:06:44Z")

</div>

> [@austintatious](#):
>
> There seems to be a major need for a new keyword in these sort or sudo-closure as “return” would be ambiguous in control flow statements as expressions.

Yes.

Introducing `leak`, an _expression marker_, used in blocks or switch statements that yield a value.

Since `yield` is off the table, the next alternative that comes to my mind is `leak` 😁

```swift
let u: Int = if ... {
                 ...
                 leak 2
              } else if ... {
                 ...
                 leak 3
              } else {
                 ...
                 leak 5
              }

```

```swift
let u: Int = do {
                  ...
                  leak 2
                }

```

```swift
let u: Int = repeat {
                  ...
                  leak 2 // break with a value
                } while true

```

```swift
let u: Int = for i in 0..<32 {
                  ...
                  leak 17 // break with a value
                } 

```

Any statement that follows a `leak` triggers a warning or error message.

```swift
let u: Int = for i in 0..<32 {
                  ...
                  leak 17
                  print (i) // Warning - statement after leak has no effect
                }
```

---

<div class="post-metadata">

**Author:** ![georgeef](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/georgeef/32/38378_2.png) [@georgeef](https://forums.swift.org/u/georgeef)\
**Post date:** [January 12, 2025, 12:25am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/346 "2025-01-12T00:25:42Z")

</div>

This is similar to my proposal. In all examples `leak` is the last expression, but it can be more general. You can have `guard`, inner `if`, inner loops, etc., which contain `leak`. Essentially you can have `leak` in any place where `return` could appear in the body if it was a closure.

---

<div class="post-metadata">

**Author:** ![ben-cohen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ben-cohen/32/296_2.png) [@ben-cohen](https://forums.swift.org/u/ben-cohen)\
**Post date:** [January 12, 2025, 12:52am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/347 "2025-01-12T00:52:45Z")

</div>

> [@SlugFiller](#):
>
> Isn't the bidirectional type checking necessary for `if` expressions as well?

No. See my post here: [[Pitch] Last expression as return value - #149 by Ben\_Cohen](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/149)

---

<div class="post-metadata">

**Author:** ![SlugFiller](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/slugfiller/32/33973_2.png) [@SlugFiller](https://forums.swift.org/u/SlugFiller)\
**Post date:** [January 12, 2025, 1:22am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/348 "2025-01-12T01:22:46Z")

</div>

I feel like this contradicts all the people who said that removing a constraint would make things easier for people new to the language. Like, they now need to learn that some expressions that work with ternary won't work with `if`, but might compile a bit faster \[1\]. It makes me wonder how long before someone pitches removing that "limitation" too, and allow bidirectional typing for `if` and `switch` expressions.

I can't help but feel this proposal is a slippery slope that largely misses the point of the original. It would create a construct that does some things more than a ternary, and some things less, requiring that much extra brain space just to figure out whether or not it should even be used in any given situation.

* * *

1. If that's a priority. It feels like a niche thing to keep in mind.

---

<div class="post-metadata">

**Author:** ![Nobody1707](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nobody1707/32/2169_2.png) [@Nobody1707](https://forums.swift.org/u/Nobody1707)\
**Post date:** [January 12, 2025, 2:34am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/349 "2025-01-12T02:34:28Z")

</div>

I don't believe there's any desire from the core team to make `if` expressions work like ternaries. If anything, backwards compatibility is the only thing keeping ternaries from being made to work like `if` expressions.

---

<div class="post-metadata">

**Author:** ![mmmayer](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mmmayer/32/4227_2.png) [@mmmayer](https://forums.swift.org/u/mmmayer)\
**Post date:** [January 12, 2025, 2:57am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/350 "2025-01-12T02:57:01Z")

</div>

**I'm +1 on this proposal.** This continues a very swifty trend in the language that has been there since day one and on the whole makes the language more consistent given some of the recent additions to the language.

I am against making the language more complex than it already is (or I am for slowing down the rate of added complexity), however this change is just further accentuating an existing feature of the language and applying it more consistently. Yes, removing the returns can make some code harder to manually parse, but that is the language and part of learning it. There are features in every new language we learn that break our brains a bit until we groove those neural pathways.

---

<div class="post-metadata">

**Author:** ![austintatious](https://avatars.discourse-cdn.com/v4/letter/a/ce73a5/32.png) [@austintatious](https://forums.swift.org/u/austintatious)\
**Post date:** [January 12, 2025, 3:38am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/351 "2025-01-12T03:38:58Z")

</div>

I really wish we had thought through the if statements as expressions and had gone the way of requiring an explicit marker (some have suggested something like `do`) but I would have been happy if it was `get` and `return` would be `give`

```swift
let u: Int = get if ... {
                 ...
                 give 2
              } else if ... {
                 ...
                 give 3
              } else {
                 ...
                 give 5
              }

```

I don't think I like 'leak' but it is sure a lot more clear and having nothing at all (which I oppose).

---

<div class="post-metadata">

**Author:** ![jaleel](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jaleel/32/45076_2.png) [@jaleel](https://forums.swift.org/u/jaleel)\
**Post date:** [January 12, 2025, 1:10pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/353 "2025-01-12T13:10:15Z")

</div>

> [@austintatious](#):
>
> but I would have been happy if it was `get` and `return` would be `give`

If we bringing learning topic IMHO `last expression as return value` is more valuable for beginners as people can face it in lots of other languages, than number of keywords only in one language.

---

<div class="post-metadata">

**Author:** ![austintatious](https://avatars.discourse-cdn.com/v4/letter/a/ce73a5/32.png) [@austintatious](https://forums.swift.org/u/austintatious)\
**Post date:** [January 12, 2025, 4:46pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/354 "2025-01-12T16:46:31Z")

</div>

Unfortunately that just isn’t true. I wish it was true that teaching Unexplicit things was easier than teaching explicit things. It would save me some time. I do not teach that you can elide types from variable declarations anymore. I do not teach about trailing closures at first (or maybe at all, I avoid them). I’m even careful around autoclosures when creating sample code for my students. I need them to see what is actually going on. Only after they solidly understand what the compiler is guessing, do I start to demonstrate that certain things can be removed.

Explicit code is always better, safer and easier to teach. it allows me to get much more quickly to the concepts.

Builder functions are still exceedingly difficult to explain. Especially when control flow statements don’t work the same. There is too much incredible magic happening behind the scene with builder functions.

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [January 12, 2025, 5:13pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/355 "2025-01-12T17:13:20Z")

</div>

I hear what you’re saying, but you’re also showing some of the limits of “what’s best for the first month of intro programming” as a rubric for language design. I completely agree with teaching your students to write out variable types, but that doesn’t mean that type inference shouldn’t be in the language at all. Teaching intro students a curated subset and then gradually expanding it as they begin to master concepts is time-honored practice.

---

<div class="post-metadata">

**Author:** ![CharlesS](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/charless/32/6488_2.png) [@CharlesS](https://forums.swift.org/u/CharlesS)\
**Post date:** [January 12, 2025, 5:29pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/356 "2025-01-12T17:29:00Z")

</div>

> [@austintatious](#):
>
> Unfortunately that just isn’t true. I wish it was true that teaching Unexplicit things was easier than teaching explicit things. It would save me some time.

Explicit is better than implicit. If [even Python, the language that decided to get rid of curly braces in favor of letting the interpreter figure things out solely from indentation, agrees on that](https://en.wikipedia.org/wiki/Zen_of_Python#Principles), we should probably take note.

---

<div class="post-metadata">

**Author:** ![austintatious](https://avatars.discourse-cdn.com/v4/letter/a/ce73a5/32.png) [@austintatious](https://forums.swift.org/u/austintatious)\
**Post date:** [January 12, 2025, 5:57pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/357 "2025-01-12T17:57:06Z")

</div>

The idea behind Swift has always been for me safety and clarity. Being Explicit always wins that one. When those two things are violated, it is not the "Swifty" way of doing things.

I am trying my best to refrain from having a discussion of past 'features' that were added to the language that in retrospect should have never been (either because they degrade compile times or they reduced clarity and clarity and make the language more akin to messy C than to the philosophy of Swift). I am really just trying to focus on not introducing more harm to the language. I know it's difficult to advocate for such harm reduction without bringing up these cases, but when they are discussed I think it mentally taints our bias towards "well we let these types of features in before, and I like them, so let's do this as well."

So I will say it again, eliding `return` causes measurable harm to the language, debug cycles, and if those are not accepted reasons (because they can be viewed as just opinion), then at least considered that it is not needed. Allowing it to be elided does not solve a problem, it encourages a problem, creates ambiguity, and may have special circumstances that create valid code when there should not be. There is no functionality or feature to be gained by allowing it to be elided.

The compiler can figure out when you need `await` because the function will be decorated with `async`. We choose to require it because disclosure of that yield point is necessary to quickly seeing control flow. This is truly no different than `return `. Return has consequences to what value is being thrown around. Not just reading it but also searching for it! The language should refrain for guessing what I want to do with my code.

Furthermore, I do more than just not teach them at first, I briefly cover the less Swifty way of doing it (i.e eliding things), and then move on to encouraging to never do it. Avoiding eliding code just makes for stronger code that is better read by a team and also allows you to -- for example -- debug easier in a TextEditor (which is what I do when I compile on Ubuntu).

Sometimes I wonder how many of us are working on large projects where little details like this an have huge impacts on debug cycles. I can guarantee you that working on projects with hundreds of files with with 10s of thousands of lines makes bug prone features like eliding important control flow keywords much more difficult (worse types!)-- even for code that I wrote myself, looking up return types for functions that are getting assigned to variables can be a headache (and I am guilty of it!)

Let's not continue this trend.

I do think I need to yield on this topic, however, I am just repeating myself. And even I understand that adding this features will only make it harder to find return types, make code less safe, create silly debug situations, but we are all smart and can figure it out. The language will carry on.

Thank you for all who took the time to read my position. You have been very kind to humor me.

---

<div class="post-metadata">

**Author:** ![georgeef](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/georgeef/32/38378_2.png) [@georgeef](https://forums.swift.org/u/georgeef)\
**Post date:** [January 12, 2025, 6:08pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/358 "2025-01-12T18:08:43Z")

</div>

Seems that it is time for summaries, so here is mine.

The new syntax allows me to inject log messages inside expressions, which is useful for debugging. Am I going to use this feature? Probably yes, with some caution, since it works in some context but not in other seemingly similar contexts. Certainly yes, when I am desperate during debugging and any additional help is welcome.

Am I going to use it as a new debugging paradigm? Definitely not. I prefer to have a sound debugging strategy and to implement it in a systematic way. The opportunity to inject some log messages inside an existing expression is not sufficient.

The new syntax also allows me to introduce side-effects inside expressions. This in turn could make the code more compact. Do I need this feature? Quite the opposite. I try my best to separate update logic from pure expressions. I had many times this great feeling of eliminating code lines without losing functionality. It was many years ago, even before Swift, and I don't miss it. Minimizing the number of lines or bytes is not my target. For this purpose I could remove comments and I could refactor variable names to a single letter. It is mechanical, easy, and very effective. It is also very counter-productive. Separation of ideas is much better than local code reduction. It allows for better refactoring at a higher level, and eventually also reduces the code size by much more than a few lines here and there.

Sooner or later I'll find Swift code with if/switch expressions mixed with side-effects. Can I work with it? Definitely yes. Even if this style becomes popular, it will be quite limited. Anyway the code cannot be too complex and correct at the same time. So I am not worried about the widespread of a feature that I don't like.

The new syntax also allows me to omit the `return` keyword in some places, if I want. I definitely don't want to do so, actually I'm going to fill-it in where it is missing. But can I work with code copied from others? For my personal projects definitely yes. Either I use some code as it is (and I learned to not touch something that works ok), or if I want to refactor it, I'll make many more changes than adding `return` here and there. In addition, the rules for where `return` can be omitted are pretty simple, so it is mostly a mechanical process. So not a big deal.

For team projects it is different though. What starts as a personal taste can quickly escalate and become an existential problem. If my team likes to omit `return`, I don't have many options. Re-formatting the code every time I want to look at it is not a solution. Going after the others and filling in `return`s is not a solution either. Even worse, if I cannot read code without `return` and my team cannot read code with `return`, there are only two options left. The first is to adapt to the others, the second is to find another team and project.

Overall, the last expression with optional `return` has the potential to create big annoyances in team projects. The if/switch/do expressions introduce features that I hardly need, but nevertheless they are manageable.

---

<div class="post-metadata">

**Author:** ![CharlesS](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/charless/32/6488_2.png) [@CharlesS](https://forums.swift.org/u/CharlesS)\
**Post date:** [January 12, 2025, 6:40pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/359 "2025-01-12T18:40:52Z")

</div>

+1000

[Previous page](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958.md?page=16)

[Next page](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958.md?page=18)
