How to break a switch body?

If looks only the h way works in the following code.
Am I missing something?

const std = @import("std");

fn f() void {
	var n:usize = 5;
	switch (n) blk: {
		else => {
			break: blk;
		},
	}
}

fn g() void {
	var n:usize = 5;
	blk: switch (n) {
		else => {
			break: blk;
		},
	}
}

fn h() void {
	var n:usize = 5;
	blk: { switch (n) {
		else => {
			break: blk;
		},
	}}
}

pub fn main() void {
	f();
	g();
	h();
}
1 Like

Check out this thread if you haven’t already: Multiline IF blocks as expressions?

From the Zig documentation:

All branches of a switch expression must be able to be coerced to a common type.

We’d have a real conundrum on our hands given that you can return values from a switch. For instance, if we assign from a switch statement but could also arbitrarily break them, you could have inconsistent return values (one would be void, the other might be int).

To be clear, when you say “break from a switch”, are you referring to the classic case of fallthrough (which Zig does not have)?

1 Like

Since there is no case fallthrough in zig, breaking from the inner block also has the same effect:

switch (n) {
	else => blk: {
		if(someCondition) break :blk;
		...
	},
}

In my opinion this is more readable because the label is closer to the break.

2 Likes

This is the preferred way of doing it.

Since there is no case fallthrough in zig, breaking from the inner block also has the same effect: …

I’m aware of this. But there are tons of branches. Labeling every one seems verbose.

From the Zig documentation:

All branches of a switch expression must be able to be coerced to a common type.

We’d have a real conundrum on our hands given that you can return values from a switch. For instance, if we assign from a switch statement but could also arbitrarily break them, you could have inconsistent return values (one would be void, the other might be int).

I’m not sure I totally understand these. Is there any bad of the pattern used in function h?

To be clear, when you say “break from a switch”, are you referring to the classic case of fallthrough (which Zig does not have)?

I mean making execution jump out of the whole switch block.

No problem, let’s have a look at some pseudo code to understand what is meant by that statement in the documentation. I’m using pseudo code because the example would not compile, so this is a counter factual.

Let’s say that I want to have a variable assigned to the result of a switch statement:

var result = switch (value) {
    /// Branch 1: 
    ///     return an integer

    /// Branch 2:
    ///     if predicate, break the switch
    ///     else return an integer
}

If we go through branch 1, we’ll return an integer - so the type of “result” would be the same integer type.

However, in branch 2, if the predicate is true, we’ll just break. So that leaves the question - if we break, then what is the type of “result”? It’s not an integer, because we aren’t returning an integer, we’re just breaking out of the statement (it would probably be “void” because we aren’t returning anything). That’s the problem - one code path returns integers, the other code path can return void.

So returning to the documentation:

All branches of a switch expression must be able to be coerced to a common type.

We can’t coerce the types to be each other (void → int or int → void).

Thanks for the more detailed explanations. So the switch and if-else blocks are always treated as expressions. That is reasonable.

1 Like

related:

4 Likes