# \[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:** 18

<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 12, 2025, 6:42pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/360 "2025-01-12T18:42:28Z")

</div>

> [@sspringer](#):
>
> 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.)

> **longer comment**
>
> Sorry I am repeating myself, that would eliminate a point that @austintatious [brought up](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/335) (“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?”), would remove some ambiguity that exists even without this pitch, and a warning should not hurt old code bases too much. I would like to know what people think about it, if it should be added to the pitch (regardless of whether you are in favor of it), or if it would even be a good thing independently of if. To show some code:
> 
> ```swift
> @discardableResult
> func f() -> Int { 1 }
> 
> let g = { () in f() }
> 
> ```
> 
> To my opinion, the type of `g` should not have to be guessed without a warning.

**Update:** I added an according [Swift issue](https://github.com/swiftlang/swift/issues/78597) as this question might be seen independently from this topic and it also might be a change too small to formulate a pitch for it. Maybe not to be discussed further here, although it might be a point to add to the pitch text.

---

<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, 7:12pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/361 "2025-01-12T19:12:24Z")

</div>

One of the arguments for the optional `return` at the last expression is consistency with one-liner closures.

The following is a non-sense feature, with an equally strong consistency argument. We make optional the `return` at the _first_ expression of a function/closure. Of course this is not so useful, since there is no code after a return anyway, but the compiler allows it with a warning, and sometimes an immediate return is temporarily useful for debugging. Anyway usability is a different story and a different argument. So let us focus on consistency. This feature has the nice property of extending an already existing property from one-liner closures to all functions. It fills a gap in the language. One-liner closures are not an exception anymore, since all functions/closures get the same property. (Of course all this paragraph is non-sense.)

I buy the argument of usability, I don't buy the argument of consistency. Both the _first_ and the _last_ expression are extensions of the _single_ expression. Both extensions are equally _arbitrary_, I wouldn't call them _consistent_. And there are many more arbitrary extensions (any one, but only one, of the `return` can be optional; the `return` closer to the center can be optional; if one `return` is omitted all must be omitted; etc., etc.)

---

<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, 7:23pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/362 "2025-01-12T19:23:25Z")

</div>

> [@georgeef](#):
>
> Seems that it is time for summaries, so here is mine.

Please do not post summaries.

Almost by definition they make an already-long thread longer without injecting new information. In theory, they can provide a useful recap for people new to the thread, but in practice they of course emphasize the preferences of the summarizer, hence you get _multiple_ summaries, negating the benefit and leading to dueling summaries.

---

<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 12, 2025, 9:45pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/363 "2025-01-12T21:45:27Z")

</div>

> [@ben-cohen](#):
>
> Please do not post summaries.

You are right, sorry for starting that.

---

<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, 9:59pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/364 "2025-01-12T21:59:06Z")

</div>

Attention conservation notice: long post, sorry.

I want to take a step back for a moment and talk about a more general principle that's behind this and many evolution proposals, which is the pursuit of an overarching goal of Swift as a low-ceremony language. As a way to illustrate this I want to use a very short snippet of Objective-C (which was recently [posted](https://bsky.app/profile/dimillian.app/post/3lfeuxp2nk22w) by @Dimillian on Blue Sky).

```swift
- (BOOL)hasSupplementaryViewAfter {
  NSInteger cellIndex = [self.elements indexOfObjectPassingTest:^BOOL(MSCListItemElement * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    return obj.elementCategory == UICollectionElementCategoryCell;
  }];
  return [self.elements indexOfObjectPassingTest:^BOOL(MSCListItemElement * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    return idx › cellIndex && obj .elementCategory == UICollectionElementCategorySupplementaryView;
  }] != NSNotFound;
}

```

In just this small snippet of code there are maybe a dozen different kinds of "ceremony" that Swift avoids. In most of these cases, it does so by trading off what you might consider clarity or safety. To run through them:

- Swift defaults members to be methods rather than statics, so the leading `-` sigil is unnecessary. This is the natural default, without having much of a clarity downside.
- Type inference means you do not need to declare the type assigned from `indexOfObjectPassingTest`. This is less clear – you cannot just by reading the code tell that this method returns `NSInteger` (in fact in Swift, it would be an `Optional` – let's come back to that).
- Explicit `self`. The concerns about Swift not requiring that have been [thoroughly litigated](https://forums.swift.org/t/rejected-se-0009-require-self-for-accessing-instance-members/930).
- The verbose name of `indexOfObjectPassingTest`. In Swift the [API guidelines](https://www.swift.org/documentation/api-design-guidelines/#naming) discourage needless words. In this case Swift's equivalent is `firstIndex(where:)`, since the call site's use of a closure makes it clear there is a test being applied. Indeed, with trailing closure syntax, even the `where` can be omitted.
- Type inference appears again, in the most extreme case of the types of the block. Swift allows you to name the arguments in a closure without typing them, or to use `$0` as a shorthand isntead of naming them.
- Within that type signature, we see pointers and nullability. At first this might not be seen as a clarity trade-off, except that a C++ programmer might not agree. Swift values are passed into functions by const ref by default, but this is not explicit in the code unlike in C/C++/Objective-C.
- And Swift's closure syntax is simpler in other ways, requiring only an opening brace, no `^` or parens. Another possible source of confusion since it makes closures (by design) look like control flow syntax.
- Within the first block we get to the subject of this pitch: the first `return` In Swift this first block is a single-expression closure, so the `return` can be omitted. This thread includes many reasons why this can be considered less clear, confusing for beginners etc.
- Swift does not require semicolons, of course. Even this seemingly simple thing introduces complexity and ambiguity into the language. Without semicolons, new lines are syntactically significant (i.e. you can't write on one line `let a = 1 let b = 2`). However, the fact that they don't at first thought _seem_ to be significant – that most of the time you think of Swift dropping the semicolons as just casting off a redundant vestigial tail – is a testament to how well the grammar and parser handles this. But there are surprising edge cases you can hit on rare occasions.
- Finally we get to the actual subject of this pitch – the second return. This code is written to first get a value, then use it for the return value. With this proposal, that `return` could be dropped.
- At the end, there is a `!= NSNotFound`. In Swift, this would be represented by the `nil` case of an `Optional` return value. But even here, you could make the case this is less clear. The words "not found" clarify the meaning of the return value. Perhaps `firstIndex(where:)` should define a custom enum, with `.found(Index)` and `.notFound` cases, instead of re-using `Optional`.

So let's look at the Swift equivalent and talk about that. First, let's take a fairly literal approach, which came [via](https://hachyderm.io/@inthehands/113811109797502947) @Paul_Cantrell:

```swift
func hasSupplementaryViewAfter() -> Bool {
  let cellIndex = elements.firstIndex {
    $0.elementCategory == .cell
  } ?? .max
  return elements.enumerated().contains { idx, obj in
    idx > cellIndex && obj.elementCategory == .supplementaryView
  }
}

```

Most people\[1\] will look at that code and agree that it is vastly better – clearer, more readable, more _enjoyable_ – than the Objective-C code. Now, I am putting a finger on the scales by comparing this code to Objective-C, which is _notoriously_ verbose. But hopefully this helps illustrate how important Swift's low ceremony approach is.

Yet there's no doubt that nearly all of the reduction in verbosity has, item by item, a "this is less clear, this could cause confusion" argument to be made against it. What's more, any one of these small improvements could be dropped, and the code wouldn't be much the worse for it. But if you dropped them all, the code ceases to be better. The changes all combine together to give Swift its low-ceremony feel. Their effects compound.

The challenge with this situation–that to achieve the low-ceremony feel we desire, we must bring together many low-impact things–is that each one has downsides. Individually they have a small impact, so for each one, one might say “we shouldn't do this because ” due to those downsides. For each one, you can say “come on, is that return/semicolon/paren/logner name really a problem? it's NBD, and consider downsides”. Each one is small, and so easy to argue against in isolation, but they add up to be huge.

Another fair counter to this is "OK the benefits multiply, but don't the risks too". But they do not. Firstly, because they are oughtweighed by the fact that ceremony free code is _clearer_. We'll come back to that point. But also, the cited risks often _don't_ multiply. For example the beginner issue of needing to learn about single-expression closures, or of `if let x {`: these are learned _once_. Whereas you then benefit from them forever. You learn a language once, then use it for years. Over-optimizing for beginners is the wrong trade-off.\[2\]

Another position to take is "OK fine, where we are now is good, but let's not go further". And to that I also disagree. Swift today is great, but I reject the idea that it doesn't need to be _better_. There are still places where devs coming from other languages say "huh, ok, but this other language I used to use had ". We should not deny Swift users the benefits other languages have, out of an excess of unjustified caution that Swift needs to be settled, or because downsides can be cited about this latest change that could also be cited about all the other features Swift already has that come together to make it the language it is today.

Ceremony reduction isn't the _only_ direction Swift can go in future. There have been evolution proposals to increase ceremony where it is important. For example, the addition of `any` to indicate use of existentials, based on the experience that lack of that ceremony was leading people to do the wrong thing and default to existentials when they probably wanted generics. But overall, the general direction of Swift is to reduce ceremony for generics too – from protocol extensions in Swift 2, to the recent addition of [primary associated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0358-primary-associated-types-in-stdlib.md).

* * *

Let's talk about the word clarity. I think this term tends to get misused, and conflated with verbosity. It's sometimes taken as a given that being more explicit always means being more clear. Let me try and argue it's often the opposite.

I called that first translation "literal" because this code does something to remain similar to the original: it replaces the `nil` returned from `firstIndex` with `Int.max`.

Interestingly, this is one place where the Swift code is _more_ verbose than the Objective-C code. `indexOfObjectPassingTest` returns a [sentinel value](https://en.wikipedia.org/wiki/Sentinel_value). And it so happens that this sentinel value is defined to be `Int.max`, and the code later takes advantage of this: `idx > cellIndex` is going to be false for all values when the value returned is `NSNotFound`. But this is partly luck – the sentinel value could have been `-1`, as it is in a lot of languages and APIs. Swift, by using optionals instead of sentinel values, forces this information out into the code, explicitly.

This is an example where Swift made a choice that is certainly harder for beginners. Sentinal values, for all their faults, are easily explained in a sentence or two. Whereas if you were showing this to a beginner, you may have to take a lengthy pause to explain the entire concept of optionals. But it would be a mistake to think that we should therefore use sentinal values, because they're easier to teach.

Once the optional forces the `.max` explicitly into the code, you can then make another leap. This code could be clearer if either it avoided doing that redundant iteration over the whole array in the case of not found _or_ just `!`-ed the return value because that test can never fail (e.g. there is always one `.cell`). Let's say it's the former. It can be rewritten like this:\[3\]

```swift
func hasSupplementaryViewAfter() -> Bool {
  if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
    elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
  } else {
    false
  }
}

```

This code is an improvement, both in performance (objectively) and clarity (subjectively). And it came about in part because of Swift's use of `Optional` forcing the surfacing of the detail about `.max`. But also this refactoring was obvious because the rest of the code was so short and clear. You can see all of it at once, because you are not distracted by the huge amounts of ceremony the Objective-C code did. So you spot improvements or bugs more easily.

**Clarity is correlated to, but not synonymous with, code being explicit**. The Swift code was less explicit in various ways, which made it shorter and easier to read, and therefore clearer.

This isn't always the case. Sometimes brevity is clearer, sometimes verbosity is clearer. `let doubled = numbers.map { $0*2 }` is clearer than `let doubled = numbers.map { i in i*2 }`. The `i in` does nothing but distract. But sometimes the terse `$0` syntax is worse. For example, I always name the inputs of `reduce` explicitly even in one liners i.e. `let sum = numbers.reduce(into: 0) { total, i in total += i }` because I can never keep straight which is `$0` and which is `$1`.

This shows another important point: you are still responsible for writing clear code. Nearly every technique Swift has for reducing ceremony can be abused, just like `$0` above. This pitch will be another example. Just because a feature can be used to write obfuscated code does not been it is a bad feature, just that it can appear in bad programs.

* * *

Finally, I'll do a bit more boosting for this pitch. The code above has

```swift
  if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell })

```

Why did I write it like that? I'll be honest, it's so I could make it a single-expression `if` statement and avoid the return. Really, I think it's better to break it onto a separate line:

```swift
func hasSupplementaryViewAfter() -> Bool {
  let cellIndex = elements.firstIndex { $0.elementCategory == .cell }
  if let cellIndex {
    return elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
  } else {
    return false
  }
}

```

That's clearer. But now we have our ceremony back. The `return`s here don't add value, they interfere with readability. This pitch would remove them entirely and would be in keeping with Swift's long term vision of being a low ceremony language. I think this is better:

```swift
func hasSupplementaryViewAfter() -> Bool {
  let cellIndex = elements.firstIndex { $0.elementCategory == .cell }
  if let cellIndex {
    elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
  } else {
    false
  }
}

```

I get that many disagree, that to them the returns are load bearing. But in Swift, returns can be omitted. This isn't something newly proposed here – that is how the language is, today. We just have an exception they can't be omitted when that first declaration is present. And that isn't very well justified.

One more example. Here is some code from the end of a function:

```swift
func hit(_ ray: Ray, _ bounds: Range<Double>) -> Hit? {
  // snip...

  // Find the nearest root that lies in the acceptable range.
  var root = (h - sqrtd) / a
  if !bounds.surrounds(root) {
    root = (h + sqrtd) / a
    if !bounds.surrounds(root) {
      return nil
    }
  }
  
  let point = ray.at(root)
  return Hit(
    at: root,
    by: ray,
    normal: (point - center) / radius,
  )
}

```

There are two `returns` here. One adds immense value. Not just semantically: its presence and highlighting as a keyword draws attention, because it is important. It is an early exit, a place to care about when reading and writing. This is the point in the code where we determine the ray did not hit the object. This is why people like `guard`, though it's not a good fit here.\[4\]

The ending `return` has no such role. Functions return when they reach the end. That doesn’t need re-stating, and doing so increases ceremony without increasing clarity.

* * *

1. By no means all. There are still some enthusiasts out there. 

2. Note, _over_-optimizing. We should still pay attention to the learning curve and progressive disclosure and make sure Swift remains an approachable language. It's a trade-off, not an absolute. But (and this is subjective) I just don't believe claims that newcomers to Swift are confused by single-expression returns, once they are properly explained. 

3. I would not be surprised to find there's a bug in my refactoring here. That shouldn't detract from the point: logic bugs can happen, of course. By removing the ceremony, we give ourselves more of a chance at spotting them. 

4. Lots of Swift devs like to use guard for every early exit, and I get why (it's part of the motivation for this pitch). But I think forcing the inversion of the predicate just to use guard is unnecesarily obfuscatory in some cases including this one.

---

<div class="post-metadata">

**Author:** ![davedelong](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/davedelong/32/38708_2.png) [@davedelong](https://forums.swift.org/u/davedelong)\
**Post date:** [January 12, 2025, 10:07pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/365 "2025-01-12T22:07:30Z")

</div>

> [@ben-cohen](#):
>
> Swift defaults to internal visibility, so the leading `-` sigil is unnecessary.

That's not what the leading `-` means at all. It means it's an instance-level method, as opposed to a type-level method (which would use `+`).

---

<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, 10:08pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/366 "2025-01-12T22:08:18Z")

</div>

D'oh, you're right! Thank you. Though it's still a default-vs-not thing in Swift's case. Will edit.

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [January 12, 2025, 10:38pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/367 "2025-01-12T22:38:31Z")

</div>

> [@ben-cohen](#):
>
> One adds immense value. Not just semantically: its presence and highlighting as a keyword draws attention, because it is important. It is an early exit,

So, here's the rub. Let's go back to the earlier example:

```swift
func hasSupplementaryViewAfter() -> Bool {
  let cellIndex = elements.firstIndex { $0.elementCategory == .cell }
  if let cellIndex {
    return elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
  } else {
    return false
  }
}

```

Though, in your view, _both_ returns here are ceremony, it is also the case that the first `return` is an early exit.

Before SE-0380, there would have been a singular least-ceremony way of writing this code (two `return`s, no `else`); today, there is a second (one `return`, one `else`); and with this feature, there would be a third, leastest way (no `return`, one `else`).

There is low ceremony, but I think we should be attentive that the pursuit of _least_ ceremony doesn't lead to so many stylistic choices that choosing _how_ to writing anything becomes a ceremony in itself.

---

<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, 10:45pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/368 "2025-01-12T22:45:17Z")

</div>

> [@xwu](#):
>
> Though, in your view, _both_ returns here are ceremony, it is also the case that the first `return` is an early exit.

I don't think "in a line above" and "earlier" are quite synonymous. The two branches are at the same point, semantically.

In a sense I think of the `else` branch as the "early exit" in so much as you might rewrite it like this in your head (and it might be better written this way, too):

```swift
func hasSupplementaryViewAfter() -> Bool {
  guard let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell })
  else { return nil } // herein lies the debate about implicit return in guard else

  elements[cellIndex...].contains { $0.elementCategory == .supplementaryView 
}

```

---

<div class="post-metadata">

**Author:** ![Paul\_Cantrell](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paul_cantrell/32/16764_2.png) [@Paul\_Cantrell](https://forums.swift.org/u/Paul_Cantrell)\
**Post date:** [January 12, 2025, 10:46pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/369 "2025-01-12T22:46:25Z")

</div>

Thanks for this thoughtful post, Ben. It does help contextualize this pitch quite a bit.

Something that feels worth highlighting, maybe relevant:

In the original code, only the second array search in each method explicitly mentions `NSNotFound`, but as Ben pointed out, _both_ searches rely on it: `cellIndex` can be `NSNotFound`. The code _implicitly_ relies on how `NSNotFound` happens to compare to actual array indices.

That implicit behavior raises a question: **Did the author do this on purpose?** Or is the behavior accidental? In the Objective-C version as written, it’s impossible to tell!

In both my very literal Swift rewrite and Ben’s more idiomatic (and, yes, clearly better) version, that question is not present. Optionals force the code’s author to make it clear that the behavior in the “no .cell element” case is a conscious choice.

* * *

As I wrote above (and many apologies for my very grouchy first draft, truly), I do think after the initial discomfort, developers would grow accustomed to reading Swift code as pitched. That’s been my experience in other languages. Once you start getting in the habit of paying attention to the last statement, it feels natural and clear — and then, as Ben wrote in the pitch, `return` can become a useful signal for non-linear code flow.

However, I do worry a bit about the “Did the author do this on purpose?!” problem. That’s the one thing about this proposal that makes me nervous. Per the first half of the post, I can imagine situations where it’s not clear that that a function’s author _intended_ for a statement to be a return value.

I _also_ expect that Swift’s type checking would make this a very rare problem in practice. I would be really, really interesting in some kind of systematic study of whether that’s true. I wonder: could we maybe take a toolchain for this pitch and then run an automated process on a few codebases to ask, say, “What percentage of `return` statements can be removed (not just the keyword, the whole statement!) and have the code still compile?” Something like that might provide a finger-in-the-wind metric for how common the “accidental return” problem might be in practice.

---

<div class="post-metadata">

**Author:** ![shantini](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/shantini/32/38846_2.png) [@shantini](https://forums.swift.org/u/shantini)\
**Post date:** [January 12, 2025, 10:53pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/370 "2025-01-12T22:53:25Z")

</div>

> [@ben-cohen](#):
>
> That's clearer. But now we have our ceremony back. The `return`s here don't add value, they interfere with readability. This pitch would remove them entirely and would be in keeping with Swift's long term vision of being a low ceremony language. I think this is better:
> 
> ```swift
> func hasSupplementaryViewAfter() -> Bool {
> let cellIndex = elements.firstIndex { $0.elementCategory == .cell }
> if let cellIndex {
> elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
> } else {
> false
> }
> }
> 
> ```

Thanks for this write up, @ben-cohen! It was very thoughtful and I can kind of see where you’re coming from as a code author. I’m a self taught programmer and Swift has been the language I’ve used the most, starting off with Swift 2. I found this set of code difficult to understand. I thought I could walk you through my thought process, as it’s probably different than the more experienced language experts here!

1. I look at the func declaration. I see that there are no arguments but there is a Bool return type.
2. I see the `let` assignment. (I probably would have combined it with the if/let below). Because I’ve seen this before I know there’s a silent `where` before the expression in the block.
3. I see the if/else. In the first branch, I see you’re looking for whether the elements array contains something that matches some criteria. I’m unsure where the result of that is used.
4. I then see the `false` in the second branch. I’m now tipped off that the if/else returns something! I go back to the first branch and see that we’re returning a bool based on _what_ that `contains` returns.

I think otherwise, if the return statements had been there, my mental model of this function would have included “the returns are in the if/else” somewhere around step one when I was scanning.

If the issue I’m debugging is this function returning something unexpected, that extra time is kind of annoying. I’m not a language theory expert, so I’m unsure if this is the type of thing people just get used to over time. But as an app developer I would stumble over this.

---

<div class="post-metadata">

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

</div>

> [@ben-cohen](#):
>
> I don't think "in a line above" and "earlier" are quite synonymous. The two branches are at the same point, semantically.

I agree with you that that's not an early exit because it's two symmetric sides of the same initial condition, nested at the same level.

The rub that I potentially see is that it's a common style choice to eliminate `else`s when the true clause unconditionally returns, because the `else` is wholly unnecessary (unless symmetry itself is the desired goal).

Under such styles, adopting the proposed feature would create asymmetry, because it would require keeping the first `return`:

```swift
func hasSupplementaryViewAfter() -> Bool {
  let cellIndex = elements.firstIndex { $0.elementCategory == .cell }
  if let cellIndex {
    return elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
  }
  false
}

```

If one of the suggested reasons in favor of this proposal is that it makes early returns stand out, this style sort of does the opposite by privileging the true branch of the `if` when it otherwise shouldn't be.

Maybe the answer to this is simply "don't adopt the conflicting `else`-eliding style choice", but the fact that this proposed change doesn't slot neatly into some common styles does give me some pause. On the other hand, that style might be a _result_ of the lack of last-expression implicit return, so that concern might not be terribly relevant.

---

<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, 11:02pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/372 "2025-01-12T23:02:55Z")

</div>

I appreciate your post but it demonstrates once again the mistake of imagining the solution to a problem that does not exist within the context of EXTREMELY short examples. Which is understandable, because it is (1) difficult to imagine large things and (2) nobody has time to type out real world-examples.

> [@ben-cohen](#):
>
> Let's talk about the word clarity. I think this term tends to get misused, and conflated with verbosity. It's sometimes taken as a given that being more explicit always means being more clear. Let me try and argue it's often the opposite.

That is true, but the opposition participants aren't confusing verbosity and clarity. We really do mean clarity here.

The following example is NOT clear when `return` is elided. It is difficult to see, because we're looking at an extremely short example, it is the equivalent of eliding `await`. Both really are just ceremony as you put it. (Eliding await would be less messy in terms of control flow you just lose your signal to the code reader nothing more)

> [@ben-cohen](#):
>
> ```swift
> func hasSupplementaryViewAfter() -> Bool {
> if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell })
> elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
> } else {
> false
> }
> }
> 
> ```

To better exemplify the issue lets lengthen this a bit to something akin to real world.

```swift
//In another library (no idea if this is discardable or not)
@discardableResult someBool(flip: Bool) -> Bool {return !flip}

var doSomething: Bool = false
func hasSupplementaryViewAfter() -> Bool {
	if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
		if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
			elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
		} else if thisThat {
			someBool(flip: false)
		} else {
			if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
				elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
			} else {
				if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
					elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
				} else {
					//doSomething = someBool(flip: false)
					someBool(flip: true)
				}
			}
		}
	} else if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
		return someBool(flip: false)
	} else {
		if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
			elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
		} else {
			if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
				elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
			} else {
				if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
					elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
				} else {
          			//return here? Okay. I will add return because this is the value, I think they want.
					return someBool(flip: true)
				}
			}
		}
	}
	
//	if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
// elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
//	} else {
// if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
// elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
// } else {
// if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
// elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
// } else {
// someBool(flip: true)
// }
// }
//	}
// return someBool(flip: false)
// FIX ME: we should possibly return true here.
	
}

```

This function needs to be fixed. There seem to be multiple ways that we can go about fixing it.

We have an if-statement that can now be thought of as an expression that is the last line of our function. Someone commented out my return line -- accidentally?--, it was just compiling but I added a 'return' statement. I might not ever notice it. The original author who wrote this did or did not intend for someBool function to be the return value always. I'm reading through it to discover intent, but because of where it is placed, and how it is written I don't know. Maybe I just remove `return someBool(flip: false)`

This is nothing compared to what we witness in a simple one line closure. It is so clear, obvious what will be returned in a one line expression. It is CLEAR in that context.

We require 'await' because it communicates something to the author and those reading their code. It signals how everything is getting executed. Requiring `return` (and other various keywords) that yield a value, helps us understand when things are intended to exit ESPECIALLY in large functions. This is not an issue that we'd ever have with single line closures/functions.

I am certain that someone could come up with a better example and debug cycle that better demonstrates the issue.

> [@ben-cohen](#):
>
> The ending `return` has no such role. Functions return when they reach the end. That doesn’t need re-stating, and doing so increases ceremony without increasing clarity.

In one line functions, yes (not quite but we already have this as a feature so it matters not, but yes enough). The ending `return` in multi-line functions (that are not necessarily going to be 5 lines, more like 100) helps readers (not the compiler) immediately know intent. And when removed, the compiler can point you to the issue, so you can be sure that you are returning what you intend (safely with out any assumptions) and future readers of your code and those evolving your code can recognize the same and see clearly where you may be returning early. It is also common to re-arrange code. Accidentally moving what was otherwise an elided `return` towards the top would neither return nor be alerted to. It might not even be noticed if it was an @discardableResult. This would not happen in a single line function/closure as there is little to distract you. In your example, albeit short, I might not even notice it was returning it is so long.

This is what we mean by 'clarity' and 'safety'. We don't just mean "more or less verbose".

---

<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, 11:32pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/373 "2025-01-12T23:32:45Z")

</div>

Thank you for the long post, Ben. I now understand better why I don't like this proposal. It is mostly because I read code in a different way.

To me an early exit is, more often than not, a triviality. Usually a range check which returns a trivial result. I don't want to focus there. If there was a button to hide such trivialities, I would use it quite often. I do want to focus on the usual case and the useful result. And I prefer to have a clear market to draw my attention there.

This example is almost unreadable for me:

> [@ben-cohen](#):
>
> ```swift
> func hasSupplementaryViewAfter() -> Bool {
> let cellIndex = elements.firstIndex { $0.elementCategory == .cell }
> if let cellIndex {
> elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
> } else {
> false
> }
> }
> 
> ```

The first branch looks very similar to a normal call to a Void function. I need to check that this function returns something, then I need to notice that the whole if statement is actually an expression, and it happens to be at the end. Now assume that I'm reading the code of others, so I'm not yet familiar with its logic, and add some more code to make it more realistic, and it becomes totally unreadable.

There are more problems. I cannot easily cut this if statement and paste it somewhere else. I cannot add more code at the end. If I want to return in one of the two branches and do some more processing for the other, I have to add back the missing `return`. Virtually anything I want to change in this code forces me to restore the missing `return`s.

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [January 12, 2025, 11:45pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/375 "2025-01-12T23:45:40Z")

</div>

> [@ben-cohen](#):
>
> ```swift
> // herein lies the debate about implicit return in guard else
> 
> ```

Well, yes—if the argument is that "early" here isn't lexical but logical, and thus branches at the same point semantically aren't early-exiting, then by the same token the `guard...else` version has no early exit either.

Applying the principle you enunciate, then, given that we _must_ exit the function from the `else` block, the `return` inside `guard...else` is strictly ceremony here by the same token.

---

<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, 11:53pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/376 "2025-01-12T23:53:14Z")

</div>

> [@austintatious](#):
>
> I appreciate your post but [...] nobody has time to type out real world-examples.

Both the examples were "real world". One was real code posted from an app. The other was from some graphics code. This feels a bit "no true scotsman" to me.

Plenty of real world code is short, in fact one of the reasons the single-expression rule works adequately well as it does is because single-expression functions are common in Swift. Apps in particular frequently have to implement many simple functions to glue their code together. They include longer functions too for sure. It's a mix.

> [@austintatious](#):
>
> In one line functions, yes (not quite but we already have this as a feature so it matters not, but yes enough). The ending `return` in multi-line functions (that are not necessarily going to be 5 lines, more like 100) helps readers (not the compiler) immediately know intent.

No matter how long, a function has only one end. That end is generally clear to the user through the closing brace and indentation. The return does not need to punctuate the end. If that ending punctuation was important, people would dislike Swift's existing rule that void-returning functions don't need a return.

The only way you obscure that single end is by building up an implausible tower from it. And the simple answer here is: don't do that. What you have contructed is some terrible code. It would be terrible with explicit or implicit returns. This proves little about this feature other than, like most language features, it is possible to embed them in bad code, and use them inappropriately. This was the point of my bringing up `$0` in closures – an important feature that enhances clarity when used appropriately but can be abused.

edit: the code is also invalid under this proposal – it mixes explicit returns from an `if` with implicit return values in a way that would not be permitted.

---

<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, 11:58pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/377 "2025-01-12T23:58:22Z")

</div>

Except this isn't what the code would look like.

The code, if written exactly as it would be under this proposal except with explicit returns would be this:

```swift
func hasSupplementaryViewAfter() -> Bool {
  let cellIndex = elements.firstIndex { $0.elementCategory == .cell }
  return if let cellIndex {
    elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
  } else {
    false
  }
}

```

Which, under the implicit return of last expression rule would become:

```swift
func hasSupplementaryViewAfter() -> Bool {
  let cellIndex = elements.firstIndex { $0.elementCategory == .cell }
  if let cellIndex {
    elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
  } else {
    false
  }
}

```

In neither case is there more than one return.

---

<div class="post-metadata">

**Author:** ![FlorianPircher](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/florianpircher/32/39919_2.png) [@FlorianPircher](https://forums.swift.org/u/FlorianPircher)\
**Post date:** [January 12, 2025, 11:59pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/378 "2025-01-12T23:59:22Z")

</div>

I think the end of a function is much clearer/easier to identify than the fact that a block inside a function is from the `else` of a `guard` statement. Highlighting the `return` within a function is part of this proposal and that is always the case for `guard` as you would not need to use one at the end of a function.

---

<div class="post-metadata">

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

</div>

> [@Nobody1707](#):
>
> Except this isn't what the code would look like.

Exactly—you've re-emphasized my point. This proposal privileges specific style choices and prevents other common ones.

And maybe that's fine, if that's the choice we want to come out of this—Swift is an opinionated language. I just brought it up as an example because I haven't seen that specific style point (omitting `else`s for unconditionally returning `if`s) raised.

---

<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 13, 2025, 12:05am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/380 "2025-01-13T00:05:39Z")

</div>

My point is that it already isn't what it would look like, because the natural version is already `return if` if that makes the style privileged, then it is already privileged.

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

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