I'm trying to explain why the following doesn't generate an error:
let input = 1
switch input
{
case 0:
let message = "Zero"
print(message)
case 1:
let message = "One"
print(message)
fallthrough
default:
let message = "Something else"
print(message)
}
It sure looks to me like this code assigns a value twice to immutable message.
Each case of a switch statement constitutes its own scope, even though there are no curly braces around it. The fallthrough keyword essentially “nests” one of those scopes inside another.
Edit: actually “nests” isn’t quite right, because in a nested scope you can still access variables declared in an outer scope. So it’s more like fallthrough “jumps” from one scope to another (analogous to a function call, but of course it is not one).
I was just trying to explain this behavior, not actually trying to do anything with it. Somewhere I had read that the scope of identifiers is limited to the switch statement (inside the curly braces). Apparently, that description is not true; instead, each case has its own scope (right?)
switch 1 {
case 1:
let message = "One"
do {
print(message) // Compiles.
let message = "Something else"
print(message)
}
fallthrough
default: break
}
switch 1 {
case 1:
let message = "One"
fallthrough
default:
print(message) // Compilesn't: Use of local variable 'message' before its declaration
let message = "Something else"
print(message)
}
You can add the braces in—as long as there's a do in front. It may be helpful to imagine them being there.
let input = 1
switch input {
case 0: do {
let message = "Zero"
print(message)
}
case 1: do {
let message = "One"
print(message)
fallthrough
}
default: do {
let message = "Something else"
print(message)
}
}
do can model this as well, with scoping coming from if statements, if fallthrough only goes to default:
let input = 1
`switch`: do {
if case 0 = input {
let message = "Zero"
print(message)
break `switch` // `break` must be explicit, like in some other programming languages.
}
if case 1 = input {
let message = "One"
print(message)
// Lack of `break` means "fallthrough".
}
`default`: do { // This `do` isn't necessary but it's illustrative.
let message = "Something else"
print(message)
}
}
Like the body of an if statement, each case is a separate branch of code execution.
That might not be quite as explicit as “each case is a separate code block with its own local variables”, but it is consistent with that behavior.
Unrelated to the question of local scope of the individual case clauses, I would be inclined to use a switchexpression:
let message = switch input {
case 0: "Zero"
case 1: fallthrough
default: "Something else"
}
print(message)
This syntax not only avoids duplicative print statements, but avoids the ambiguity whether the case will return a value or fallthrough. It can only be one or the other.