Inside try blocks (and methods) I've seen programmers use Err(err)? (or similarly anyhow::bail!) to exit a block with an "always failing" condition. However control flow analysis in the compiler doesn't understand that this never returns.
A simple solution to this problem would be a prefix fail keyword. e.g.
let my_result: Result<(), MyError> = try {
let foo = if some_error_condition {
fail MyError::Something
// Not possible to use `Err(MyError::Something)?` here
} else {
12345
};
};
The compiler usually manages to figure out that such mechanisms always return an error.
This is absolutely something we'd like to do, and it's been proposed several times. The main blocker is defining the exact semantics, which is something @scottmcm has been working on in conjunction with try blocks.
there's already experimental syntax for something like this, it's not stabilized in part because we haven't decided what the best keyword is, there are quite a few options e.g. raise, throw, fail, yeet, etc. so the experimental syntax intentionally uses something basically everyone agrees is not what they want (do yeet) so we're not biased by whatever the experiment happened to pick.
I know quite a number of people who do not agree, myself included. yeet is a perfectly fine keyword, much better than throw or raise, which might give off a false idea that Rust has exceptions.
Notably the syntax (with do ) and name in this PR are not intended as candidates for stabilization, but they make a good v0 PR for adding this with minimal impact to compiler maintenance or priming one possible name choice over another.
personally, i'd be fine with yeet, but not do yeet.
What is wrong with calling errors "exceptions"? Errors returned via Result seem isomorphic to explicitly declared exceptions, like you would for example with a throws clause in Java, except that the handling syntax (?) is implicit in Java. Is there some fundamental difference?
OK but that's a performance difference rather than a semantic difference, so I don't see why that would necessitate different terminology.
I am not advocating the use of the term "exception", "error" is a more descriptive name, I just don't see how throw or raise is confusing because to me it's semantically the same operation.
exceptions are generally not the same thing as returning, because exceptions skip over functions going back up the stack until they find some try block. whereas returning always goes to the immediate caller (ignoring tail calls). rust's only form of exceptions is panic and that's not intended for normal control flow (it may abort instead of unwinding), unlike say exceptions in python or java. rust's do yeet is really shorthand for return Err (or break 'closest_try_block Err) or similar, so it is returning, not throwing an exception.
I don't see a fundamental distinction here. If you write f()? and an error is returned, it also skips over the rest of the function. Writing a ? is equivalent to not writing a try in another language. Either way the rest of the function is skipped.
It's just opt-in vs opt-out syntax. Kind of like mut is opt-in in Rust and opt-out in C++.
While this is part of the reason, ultimately the semantics are the only blocker. Once we have a proposed semantics that's ready to ship, we'll pick a syntax and live with it. It's more important to ship this feature with some syntax than for it to be blocked on which syntax.
It is no different in the current function, but a thrown exception would continue unwinding further up the call stack, while ? will return only to the caller, unless the caller also used ?, but in that case it is the caller's decision to return further, not the callee's.
Arguably one could say that in other languages the caller "chose" to unwind by not having a try..catch, but that would imply that all functions should have a try..catch in them by default, which is not the case at all.
I see your point but I do feel like there is a semantic difference.
For comparison, Swift chose to spell its non-propagating/caller-checked error-producing with throw (and spells its equivalent of ? as try), though it also has a custom ABI for it instead of making it a normal return type. But I won’t say it hasn’t been confusing for people coming from other languages, and we chose not to call it “exceptions” to (maybe) distance ourselves from people’s mental models of the implementations of “exceptions”.
In Rust the error gets propagated up the stack to the first caller that unpacks the Result by doing match, .unwrap() or something like that. Very analogous to catch. I guess you can also put the Result into a data structure. But you have to do something with it. If you simply ignore it, forgetting the ?, that's not an error but it's a warning.
You have to write something to handle the exception in Java. If you ignore the exception it's a compile error. I'm specifically talking about checked exceptions. Each function in the call stack has to explicitly declare the exception as thrown if it is supposed to be propagated further up:
I don't know what other people think, but I'm getting more and more convinced that try blocks are just a bad idea which doesn't fit into the language. it needs like five new keywords to be useful and even then it's marginally beneficial syntactic sugar at best.
afaik it only needs the try keyword, yeet is just an add-on that isn't necessary for try to be useful.
try is syntax sugar, but assuming it works as envisioned, it can greatly declutter code that needs to do a lot of operations that need error handling, but you usually don't want a lot of different handling for each error, e.g. most binaries that need to work with the filesystem.
Good to know, couldn't find it because its practically unsearchable :P. I'm biased towards fail because I've written some Prolog recently. try, fail, and proceed is the terminology that Warren's Abstract Machine uses.