3 ms·
A bit of feedback to the author: I cringe really, really hard when I see things like: #[derive(Debug)] enum RispErr { Reason(String), } One of the b
by thenewwazoo 7y ago
A bit of feedback to the author: I cringe really, really hard when I see things like:
#[derive(Debug)]
enum RispErr {
Reason(String),
}
One of the beauties of Rust's type system is that you can combine meaningful error types with the From trait to build zero-effort error handling. When you turn every error into a String, you lose information that future programmers (including yourself!) will be glad to have. I know it can be a little bit painful to get used to, but once you do, meaningful error handling will be at your fingertips.
To be helpful to readers of this comment and not just critical of the author, I recommend you don't overthink your error types when you're first starting to write your program. Think of the contents of the enum like a "scratchpad" where you list every failure mode you've encountered so far. While you're still developing, you can greedily match on every variant in your match statements, and it's fine. As you do this, you'll begin to notice themes that will inform the eventual refactor. Best of all, once you do refactor, you won't have to re-understand your code to tease out the various kinds of failures - you'll already have a list of them!
- stopachka 7y agoThanks for the feedback thenewwazoo! This sounds quite cool -- if you have the time, would you mind giving me a quick example? Want to make sure I have a full understanding of what you mean.
- thenewwazoo 7y agoBecause I'm on my soapbox, I'll also recommend implementing ToString[1] right off the bat, too. That way you can print out useful error messages immediately. For example, if your errors are: enum RispError { /// Syntax error returning line number and char SyntaxErr(u32, u32), /// Parens not balanced; contains number of parens needed UnbalancedParens(usize), } You can impl ToString straight away: impl ToString for RispErr { fn to_string(&self) -> String { match self { RispErr::SyntaxErr(l,c) => format!("syntax error at line {}, col {}", l, c), RispErr::UnbalancedParens(n) => format!("use your % key more! add {} more paren", n), } } } Then, when handling the error, you can match do_thing() { Ok(v) => { ... }, Err(e) => { // TODO handle error println!(e) } } where do_thing's signature looks like fn do_thing(...) -> Result<T, RispErr>; It takes the same amount of code, but all your error messages are in the same place, you haven't lost information about them, and refactoring to handle them becomes super easy. (n.b. I dashed this comment off without actually compiling the above, so please forgive any dumb errors =] ) [1] https://doc.rust-lang.org/std/string/trait.ToString.html https://doc.rust-lang.org/std/string/trait.ToString.html
- stopachka 7y agoOof, I love this — thank you thenewwazoo! I gotta get to sleep, but will either update the post or add a note in the morning. And of course, will do this in all future rust projects :D
- thenewwazoo 7y agoGlad to help! I am a huge huge fan of Rust. I'm almost always hanging out on ##rust and ##rust-beginners on Freenode if you want to ask questions synchronously. :)
- f-jin 7y agoInstead of implementing `ToString` directly, you should implement `Display`. A `ToString` implementation will automatically be added, and you get all the formatting goodness out of the box.
- thenewwazoo 7y agoThat's a good idea! I don't often impl Display so it didn't even occur to me.
- nibbula 7y agoAs a Lisp programmer, the way most other languages handle errors make me cringe. How could I even make a decent ice cream sundae without error recovery? But I do enjoy that 'make a Lisp' is a like a sport in every other language. http://www.lispworks.com/documentation/HyperSpec/Body/f_cerror.htm http://www.lispworks.com/documentation/HyperSpec/Body/f_cerr... http://www.lispworks.com/documentation/HyperSpec/Body/m_rst_ca.htm http://www.lispworks.com/documentation/HyperSpec/Body/m_rst_...
- csomar 7y agoUse the "?" operator. That way, you only handle errors one time (probably at the root of your program). See here: https://github.com/omarabid/rust-starter/blob/master/src/main.rs https://github.com/omarabid/rust-starter/blob/master/src/mai...
- ken 7y agoI feel there's a special type of irony in criticizing a Lisp implementation for not being sufficiently type-safe. (Or maybe it's just a generic irony ... I can't tell yet.)
- thenewwazoo 7y agoOh, I know. :) I mostly spoke up because of my own journey with Rust, and knowing how important articles like these could be to people new to the language. I totally understand why Stringifying errors is appealing in Rust but breaking the mental habit is harder than learning good habits straight away.
- MaulingMonkey 7y agoYAGNI, KISS. Lots of code never ends up caring about differentiating between different error conditions. A lot doesn't even care if the code it called failed - maybe it'll log, but the caller can continue regardless. And for a parser, I'm more likely going to want a struct with file/line info (or a full blown list of context) than a bunch of hardcoded enum cases. Having the enum in place to easily identify refactoring points is nice, but having a bunch of mostly unused variations inside the enum will mostly just distract me, and slow me down, giving me more code to re-understand regardless. In the cases where I do want to distinguish between error cases, I'd still rather wait until I have the code consuming those error enums to help guide what errors are meaningfully different - evidenced by needing different error handling paths - instead of making every caller decide which of the dozens or hundreds of edge cases they want to be handled which way(s). There are cases where your approach of distinguishing cases up front would be better - as an example, if you're limited in your ability to refactor due to the interface being part of a public API with semver restrictions to your refactoring - but there's plenty of cases where the author's approach is the correct one, where you should be cringing if you don't see things kept as simple as RispErr.
- PrototypeNM1 7y agoWhenever I've read into error writing and handling I inevitability get lost in the weeds. A tl;dr summary like you provided below would be a really helpful writeup in general, especially if it showed how it's just as easy as string errors and showed how much you get for free when doing things the right way.