The basic argument is something like "all functions should be total", but the problem is that proving that a function is total is really annoying, so we live with two other consequences instead:
1. Some functions are not total, and you just have to know the preconditions.
2. Some functions are total, but the type system is not powerful enough to prove this (possibly because they call functions in category #1).
The same discussion has been going on for decades in Haskell-land, and there's no definitive consensus, but my sense is that we'd rather tolerate a panic here and there rather than try and deal with the fuss of propagating errors or proving that our functions are total in the first place. For example, take a look at this function. I ask,
> Is this a reasonable way to write this?
struct IVec2(i32, i32);
impl IVec2 {
fn parse(d: [u8; 8]) -> Self {
Vec2(i32::from_le_bytes(d[0..4].try_into().unwrap()),
i32::from_le_bytes(d[4..8].try_into().unwrap()))
}
}
Someone from the Rust community will come in and say,> Arguably, unwrap() is unreasonable.
...but why? The function is total. It won't panic. The response is,
> ... an error should be properly handled ...
There is no error, the code can't fail. I don't know of another way to write the function without a panic hidden somewhere, either in an unwrap() or inside an indexing operation. For some reason, some members of the Rust community think of this as a problem to be solved.
It's not a problem. The unwrap() method is just too damn useful. It's too damn useful to be able to write functions that are not total. I'd say that the key here is coming up with a sensible style for when you are okay with panicking in your code, and then accepting the consequences of that decision.
("Total" means that the function returns a result for all inputs. Unwrap is not total. In Haskell, "head" is not total--it returns the first element of a list, but throws an exception if the list is empty.)