Open vishen opened 5 years ago
When you break from a while/for loop, the else branch is not evaluated. I’ve submitted #2641 to clarify that in the docs.
Would it also be useful to have the example use the else branch in the documentation? For me, who is new to Zig, it was a bit confusing having an example that didn't use the else branch here (since it breaks
out when it see's a null
and therefore never runs the else
branch).
Would it also be useful to have the example use the else branch in the documentation?
Yes, I think so
I was able to make the else
branch run using the following (https://github.com/ziglang/zig/pull/2642):
test "for else" {
// For allows an else attached to it, the same as a while loop.
var items = []?i32{ 3, 4, null, 5 };
// For loops can also be used as expressions.
var sum: i32 = 0;
const result = for (items) |value| {
if (value != null) {
sum += value.?;
}
} else blk: {
assert(sum == 12);
break :blk sum;
};
assert(result == 12);
}
However, I am wondering if this is the correct behaviour? I am probably using this wrong, but to me it looks like the else
branch is only run once the for-loop has finished iterating.
if i understand correctly the else
branch is only run if the loop doesn't break.
The most helpful way to think of it is like the else
of an if
statement, but for a while
loop. Specifically, for an error union:
while (getNextItem()) |item| {
doSomething(item);
} else |err| {
std.debug.warn("stopped processing items: {}\n", err);
}
Without the else
there would be no way to capture the err
value, which is the main reason that loops have the else
clause.
But a for loop doesn't throw exceptions?
I think the following should work as you described, however I get a syntax error; just wondering what I am doing wrong?
test "for else" {
// For allows an else attached to it, the same as a while loop.
var items = []anyerror!i32{ 3, 4, error.NoneAvailable, 5 };
// For loops can also be used as expressions.
var sum: i32 = 0;
for (items) |value| {
sum += value;
} else |err| {
warn("error in items\n");
}
assert(sum == 7);
}
/home/jonathan/z.zig:12:12: error: invalid token: '|'
} else |err| {
^
Looking at the parser.cpp file, it looks like the for-loop doesn't capture the error payload (yet?): https://github.com/ziglang/zig/blob/master/src/parser.cpp#L423-L438
// ForPrefix Body (KEYWORD_else Body)?
static AstNode *ast_parse_for_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) {
AstNode *res = ast_parse_for_prefix(pc);
if (res == nullptr)
return nullptr;
AstNode *body = ast_expect(pc, body_parser);
AstNode *else_body = nullptr;
if (eat_token_if(pc, TokenIdKeywordElse) != nullptr)
else_body = ast_expect(pc, body_parser);
assert(res->type == NodeTypeForExpr);
res->data.for_expr.body = body;
res->data.for_expr.else_node = else_body;
return res;
}
However, the while-loop does capture the error payload: https://github.com/ziglang/zig/blob/master/src/parser.cpp#L440-L459
// WhilePrefix Body (KEYWORD_else Payload? Body)?
static AstNode *ast_parse_while_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) {
AstNode *res = ast_parse_while_prefix(pc);
if (res == nullptr)
return nullptr;
AstNode *body = ast_expect(pc, body_parser);
Token *err_payload = nullptr;
AstNode *else_body = nullptr;
if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) {
err_payload = ast_parse_payload(pc);
else_body = ast_expect(pc, body_parser);
}
assert(res->type == NodeTypeWhileExpr);
res->data.while_expr.body = body;
res->data.while_expr.err_symbol = token_buf(err_payload);
res->data.while_expr.else_node = else_body;
return res;
}
For loops don't capture error values. But the control flow is the same with else
on all 7 of these:
my point is that the "while error union" example is the reasoning for else
existing on loops at all. everything else is for consistency.
Ah, sorry, gotcha!
I still find the for/else example in the docs (as of 0.9.1) to be a confusing use case, and not really demonstrate the purpose of the else clause in a for loop.
It seems to be evaluating the else
clause as a kind of useless finally
block?
I was going through ziglings.org exercises and it provides another good example for using the else block in a loop expression. See the exercise here: https://codeberg.org/ziglings/exercises/src/branch/main/exercises/062_loop_expressions.zig
It basically mentions you can use a while or for loop as an expression, by using break $value
. The else block is executed when the loop doesn't use break
.
TL:DR example:
// Given an array of numbers
const numbers: [4]u8 = .{5, 6, 9, 12};
// Search for a number higher than 10 and assign to `higher_than_ten` variable
var higher_than_ten: ?u8 = for (numbers) | num| {
if (num > 10) break num;
} else null; // this else block is executed when the for loop runs out of items. The default else block when omitted returns void.
The documentation example for
for else
(https://ziglang.org/documentation/master/#for) doesn't run theelse
branch of the code:I don't know if the
break
is then supposed to cause theelse
block to run? But currently theelse
block isn't actually run.I was just wondering what the expected behavior is here?