Search code examples
swiftswitch-statementswitch-expression

Does Swift have a Switch *expression* (as opposed to a Switch *statement*) like C#?


Hopefully by the title of this, it's clear I'm not asking does Swift support the Switch statement. I'm specifically asking if Swift supports Switch expressions, akin to what C# has.

The difference is subtle, but important. A Switch statement is a way to group lines of code based on a particular case. A Switch expression however returns a value based on a particular case.

Say you have the following enum...

enum SomeEnum {
    case a
    case b
    case c
}

Now say you need to calculate/return some value based on the particular switch statement. Currently, in Swift you have to use a Switch statement to do this...

let someEnumValue: SomeEnum = .a

let result: String

switch someEnumValue {
    case .binary:      result = "This is the 'A' type"
    case .octal:       result = "This is the 'B' type"
    case .hexadecimal: result = "This is the 'C' type"
}

print(result)
//prints 'This is the 'A' type'

As you see above, in Swift, you have to first declare let result: String but not give it a value, then within the Switch statement, you have to assign the value there. This is because a Switch statement doesn't return a value. It only executes code based on the matching case/condition. (Yes, you could also put all that in a function and simply call that, but that's separate from this question.)

C# Switch Expression

In contrast, here's how you would write it in C# with a Switch expression...

var result = someEnum switch {
    SomeEnum.a => "This is the 'A' type",
    SomeEnum.b => "This is the 'B' type",
    SomeEnum.c => "This is the 'C' type"
}

Console.WriteLine(result)
//prints 'This is the 'A' type'

As you can see here, the Switch expression directly assigned a value to the result variable since a Switch expression returns a value.

Of note: Unlike with a Switch statement where the word 'switch' goes before the variable, in a Switch expression, the word switch goes after the variable. Additionally, within the braces, the individual cases are separated by commas. Finally, every case can only be a single expression that produces a value. You can't run compound statements unless you first wrap them in their own function.

Pseudo version with Closures

A colleague of mine came up with this approach which uses an in-place closure. It's a little better, but still not quite as simple as a true Switch expression...

let result = {
    switch someEnum {
        case .a: return "This is the 'A' type"
        case .b: return "This is the 'B' type"
        case .c: return "This is the 'C' type"
    }
}()

print(result)

So my question is simple... does Swift support Switch expressions? If not, let's all suggest it to Swift.org because it makes things so much easier/simpler!


Solution

  • Edit:

    This feature is now slated to ship in Swift 5.9, and is covered briefly in this year's Whats New in Swift session at WWDC.


    Edit:

    Since answering this question, there has been movement on adding if and switch expressions to the language in the form of SE-0380: if and switch expressions, which was accepted on 2022-01-20. The implementation is available to view at apple/swift#62178, and the PR includes a downloadable toolchain, but the feature has not yet landed in a shipping version of Swift.

    I will try to update this answer again once the feature makes it into a shipping version of Swift.

    The original answer is maintained for posterity below:


    Original:

    Unfortunately, Swift does not have switch expressions, only switch statements. The Swift language grammar currently supports the following as expressions:

    • References to identifiers
    • Literals
    • Self expressions (self.<whatever>, self[<whatever>], etc.)
    • Superclass expressions (super.<whatever>, super[<whatever>], etc.)
    • Closures
    • Parenthesized expressions (expressions, in parentheses)
    • Tuples
    • Implicit member expressions (references to functions or variables that would reference self, but based on context, are allowed to skip referencing self — e.g. foo(123) as opposed to self.foo(123))
    • Wildcards (_)
    • Key paths, selectors, and key path strings

    Note that things like control flow statements, do statements, and others are not included in this list — Swift only allows those as full statements. Currently, statements can be composed of:

    • Expressions
    • Declarations
    • Loop statements
    • Branch statements (if/guard/switch)
    • Labeled statements (label: <some statement>)
    • Control transfer statements (break, continue, etc.)
    • Defer statements
    • Do statements
    • Compiler control statements (#if ..., #error, #warning, etc.)

    This is something currently intimately tied to the core of the language, so changing it would be no small feat! For this reason, this feature is also at the top of the list of the Swift "commonly rejected changes" list for control flow:

    if/else and switch as expressions: These are conceptually interesting things to support, but many of the problems solved by making these into expressions are already solved in Swift in other ways. Making them expressions introduces significant tradeoffs, and on balance, we haven't found a design that is clearly better than what we have so far.

    Despite this, this is something that comes up periodically on the forums, and has been discussed extensively (e.g. If / else expressions, which contains a lot of discussion of switch expressions too). This is something that plenty of folks want, so I'd recommend getting an understanding the latest state of things by reading through some of the threads on the forums for ideas on what/how to constructively propose, if you're interested!