#catch executes only on error type?

1 messages · Page 1 of 1 (latest)

hushed girder
#

I'm going through the ziglings exercises and was surprised by this behaviour:

My code:

`fn fixTooSmall(n: u32) MyNumberError!u32 {
// Oh dear, this is missing a lot! But don't worry, it's nearly
// identical to fixTooBig() above.
//
// If we get a TooSmall error, we should return 10.
// If we get any other error, we should return that error.
// Otherwise, we return the u32 number.
return detectProblems(n) catch |err| {
if (err == MyNumberError.TooSmall) {
return 10;
} else if (err == MyNumberError.TooBig) {
return err;
}
};
}

fn detectProblems(n: u32) MyNumberError!u32 {
if (n < 10) return MyNumberError.TooSmall;
if (n > 20) return MyNumberError.TooBig;
return n;
}
`

I thought since I covered the two possibilities for the err value (on the right side of catch) then either the 10 or the TooBig would be returned. I get the following compiler error:
error: 1 compilation errors exercises/024_errors4.zig:62:42: error: expected type 'error{TooBig,TooSmall}!u32', found 'void' return detectProblems(n) catch |err| { ^ exercises/024_errors4.zig:55:37: note: function return type declared here fn fixTooSmall(n: u32) MyNumberError!u32 {

#

As I understand, instead of my expectation, the function is returning void, which I find surprising. If I add another else to return the number n, I get a passed on the excersice. As if somehow neither of the 2 branches was run in the original version.

Modified version:

fn fixTooSmall(n: u32) MyNumberError!u32 { // Oh dear, this is missing a lot! But don't worry, it's nearly // identical to fixTooBig() above. // // If we get a TooSmall error, we should return 10. // If we get any other error, we should return that error. // Otherwise, we return the u32 number. return detectProblems(n) catch |err| { if (err == MyNumberError.TooSmall) { return 10; } else if (err == MyNumberError.TooBig) { return err; } else { return n; <------------- HERE IS THE NEW BRANCH } }; }

In summary, why do I have to return the n? I was expecting the catch to only catch error types.

final silo
#

zig is a bit silly with if and switch, when you're using if, zig doesn't check for exhaustiveness, so in your case, using switch should fix it, like

return detectProblems(n) catch |err| switch (err) {
      error.TooSmall => 10,
      error.TooBig => err,
};
#

well, it's not exclusively a zig problem, but i hope the point is clear

hushed girder
#

ok, great, nice and readable syntax, I'm going to use that from now on.
Still, I can't understand why the first version was returning void.

final silo
#

it's confusing for sure, it's talking about the type of the surrounding block

#

let's say you have sth like

const a = {};

a will actually be void here since {} is just void

#

but if you do

const a = blk: {
  break :blk @as(u8, 1);
};

now a is u8 since the whole block is "returning" a u8

#

so when you're inside a block and don't see any break :label, that block will be void as a whole

#

roughly speaking

hushed girder
#

The problem was not the value, instead, the since the block could return void on some branch, the compiler complains (correctly, in the sense that it protects me from messing up if I put another possibility in the MyNumberError).

final silo
#

eh, not really

#

the whole statement is quite dense

#

so you have return a catch |err| b

#

since the function has to return MyNumberError!u32 so a catch |err| b has to have that type

#

when you write a catch |err| b the type a and b have to "fit" (i forget what the actual term is)

#

so a has to be an error union and its payload has to be u32

#

b as a whole must be something that can be coerced to MyNumberError!u32

#

so in you original code b is void which cannot be coerced to MyNumberError!u32, hence the compiler yells at you

hushed girder
#

Thanks again @final silo !!!

final silo
hushed girder
final silo
#

yup

#

because the return statements apply to the whole function, not the block

noble lodge
#

is it correct to say that the block is, in practice, of type noreturn, but zig doesnt know it (due to limitations of if with no default else branch). (i imagine zig could theoretically become smart enough to recognise THIS case, but not every case due to uncomputability)

clever stream