Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between `let case(value)` and `case(let value)` (Swift)

Tags:

swift

Lets say we have an enum with associated values:

enum A {
    case a1(x: Int)
    case a2(y: String)
}

And some function that switches on this enum:

func doSomething(a: A) {
    
    switch a {
    case .a1(let x):
        print("a1 let \(x)")
    case let .a2(y):
        print("let a2 \(y)")
    }
}

For example:

var x = A.a1(x: 1)
var y = A.a2(y: "hello")
doSomething(a: x) // Prints a1 let 1
doSomething(a: y) // Prints let a2 hello

As you notice I can use the syntax case .a1(let x) and case let .a2(y) with no apparent difference.

So I wanted to understand: are there valid reasons to have both syntaxes, or they are purely for convenience? For example: are there a lower-level (compiler, memory) difference between those options? Are there any examples when one or the other wouldn't work as well? Thanks

like image 498
ytrewq Avatar asked Sep 01 '26 00:09

ytrewq


1 Answers

As you notice I can use the syntax case .a1(let x) and case let .a2(y) with no apparent difference.

You're right, there's very little difference between these two syntaxes — the case let syntax is for convenience.

are there valid reasons to have both syntaxes, or they are purely for convenience? For example: are there a lower-level (compiler, memory) difference between those options?

Yes, but it best applies to enum cases with more than one associated value, as it allows you to avoid having to repeat let for every single associated value in the case:

enum A {
    case a1(x: Int)
    case a2(y: String)
    case a3(x: Int, y: String)
    case a4(x: Int, y: String, z: Double)
}

let a: A = ...
switch a {
// Inner `let`:
case .a1(let x):
    print(x)

// Equivalent outer `let`:
case let .a2(y):
    print(y)

// Inner `let`s can get repetitive:
case .a3(let x, let y):
    print(x, y)

// More convenient outer `let`s:
case let .a4(x, y, z):
    print(x, y, z)
}

An explanation from the Swift language guide:

If all of the associated values for an enumeration case are extracted as constants, or if all are extracted as variables, you can place a single var or let annotation before the case name, for brevity

This is a purely syntactic convenience, and makes absolutely no difference in compilation.


If you're curious about how this shakes out from the language grammar:

  • switch statements have a list of switch cases
  • Regular cases (non-default cases) have a case label (which has one or more case items)
  • Case items are bound as patterns
  • Patterns are where the syntax branches:
    1. In the case let .a4(...) variant, the pattern is bound as a value binding pattern whose internal pattern binds as an enum case pattern
    2. In the case .a3(let...) variant, the pattern is bound directly as an enum case pattern, whose tuple pattern contents each bind a value binding pattern

The grammar technically allows for a lot of flexibility here, but the compiler ensures that you either have a var/let outside of the case label itself, or bind each variable inside of the case with a var/let internally

like image 124
Itai Ferber Avatar answered Sep 04 '26 00:09

Itai Ferber



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!