Live data from Hacker News

My Struggles with Rust

compileandrun.com

181–190 of 329 posts

Re: My Struggles with Rust

#181

Rust's aversion to exceptions is exactly like Go's aversion to generics - a strongly held position that doesn't actually make anyone's life easier.

Exception safety is complicated. I didn't believe this, having only used exceptions in higher-level languages, but enough time talking to Rust folks and I get it now. And even in C#, I know I'm getting things wrong here and there with exceptions, but it doesn't have the same impact (safety/mem leaks) due to being GC'd and memory safe.

Re: My Struggles with Rust

#182
post #39

I was under the impression that the (somewhat) verbose syntax for error handling and memory management via the type system was a necessary side effect of Rusts entire point of existence: a compiler-guaranteed safe systems language. Neither Python nor C force you in any way to pay attention to errors, making simple scripts much easier to write. I guess I'm just surprised people think that Rust should be as simple to u…

I think the complaint is more that Rust has seemingly tried very hard to make error handling "simple". But in the process it has managed to invent a whole series of new idioms and special syntax that is alien to pretty much everyone. There's a thread in /r/rust about this same article where you can look and see people suggesting all sorts of ways to write this that are split into clear sedimentary layers depending on…

I think the complaint is more that Rust has seemingly tried very hard to make error handling "simple". But in the process it has managed to invent a whole series of new idioms and special syntax that is alien to pretty much everyone. ... ways to write this that are split into clear sedimentary layers depending on when the writer learned the language.

That's been my criticism of Rust error handling. Rust's error handling system is very clever. It's logically sound. It manages to make functional programming and error handling play well together. But it's not user-friendly. For a while, it took far too much code to handle errors. So gimmicks were developed to make the necessary gyrations less verbose. These hide what's going on underneath. Thus the generations of error handling approaches.

Rust tried to avoid the complexity of exception handling, but ended up with something that's more complicated. Python programmers, who have a good exception system, notice this. In Python, you write the main case, and then you write an exception handler to deal with the error case. This works well in practice. Python has an exception class hierarchy. If you catch EnvironmentError, you get almost everything that can go wrong due to a cause external to the program. If you catch IOError, you get all I/O-related errors, including all the things that can go wrong in HTTP land.

With exceptions, if you're using some code that doesn't handle an error well, you can catch the problem at an outer level, get good information about the error, and recover. With error-value returns, after you've come through a few levels of function returns, you're usually down to "something went wrong". (Having written a web crawler, I've found this useful. A huge number of things can go wrong in HTTP, HTML parsing, SSL certificate handling, and the other manipulations needed to read a possibly-hostile web page. A crawler needs to catch all those and deal with them, deciding "try again now", "try again later", "log error and give up", or "try alternative access approach". This makes one appreciate a good exception mechanism.)

Exceptions have a bad reputation because Java and C++ implement them in ways that are inferior to Python's approach. There's no exception hierarchy. Knowing what exception something can raise is very important. Often, you don't.

Rust (and Go) are slowly backing into exception handling, as the panic/recover mechanisms acquire layers of gimmicks to make them more useful. Rust already has unwinding (destructors get run as a panic event moves outward), which is the hard part of exception handling. Thus, exceptions are more a religious issue than a technical issue.

Re: My Struggles with Rust

#183

Earlier quoted context omitted.

> your 'safe' language then happily crashes and burns everytime something goes wrong I'm not sure why you put 'safe' in quotes here; nothing about 'unwrap()' (or even 'panic') is unsafe in the context of Rust. In fact, it acts just like Python would in the same circumstances: print a developer-centric message out and exit with a bad return code. What's unsafe about that? > if you're gonna have unwrap, you basically h…

I didnt say it was unsafe, I said it crashes. Unwrap is a shortcut to let you be lazy; it exists for no other reason, and it causes application level crashes in way that is very much easier to avoid in other languages. That 'catch_unwind' exists is evidence that some kind of panic recovery is necessary... and I wonder how often you hit it from a real panic, vs. a stray lazy unwrap? Whats your justification for unwrap…

>An application error (returned null) shouldn't abort your application with a hard error, no logs. Its just plain poor practice to use unwrap().

What? That's what every other language does.

  file.open("foo").read_line()
If open fails it'll either throw or return null which will then cause read_line to throw a null pointer exception. Rust just makes things explicit here.

Though it might be interesting if there was a special opt-in Deref impl for Result and Option so people could omit the unwrap and just get it implicitly, for the occasions when you don't want that explicitness.

Re: My Struggles with Rust

#184

It makes me sad to see the example. This is why I maintain `.unwrap()` is one of the worst things in rust. ...because people use it; and then say; 'but don't use unwrap...'; and then use it, and your 'safe' language then happily crashes and burns everytime something goes wrong. Blogs and documentation are particularly prone to it. Result and option types are good; but if you're gonna have unwrap, you basically have t…

Yeah I found this odd too. Rust docs say unwrap() shouldn't really be used, but through the rest of the documentation examples it's used everywhere. I suppose it's to keep the documentation simple and focused

Yes, that's why. When ? Can be used in main, we will switch to that en mass.

Re: My Struggles with Rust

#185
post #94
post #4

My main gripe with Rust so far has been the unnecessary profusion of Result types, making it hard to process and forward errors. Case in point: the example in the article from the rust documentation that converts errors to strings just to forward them: https://doc.rust-lang.org/book/error-handling.html#the-limit... In practice, I find a type like Google's util::StatusOr ( https://github.com/google/lmctfy/blob/master/…

> Case in point: the example in the article from the rust documentation that converts errors to strings just to forward them This section: - Shows you how to define your own Result types. They have chosen a String as an example of what you could use as an error type. In practice nobody uses "String" as an error type. - Concludes by defining a custom error type to use instead of a String. I guess you didn't read that…

Using String as an example error type seems like a bad choice if nobody actually uses it in practice, though -- it's just leading you down the garden path.

Personally I found the error handling section of the documentation confusing and frustrating -- it works through three or four different approaches pointing out issues with them as it goes, and it's hard to tell when it's discussing a simple-but-wrong approach as motivation for the following more-complex-but-correct one, and when it's actually recommending you use the approach. Plus it finishes with an approach with nice properties but an awful lot of boiler plate conversion code, which left me thinking 'surely there must be a better way'. IMHO the error handling section of the rust docs should describe just one way to do things, and it should be the standard way everything uses so your code interoperates with library errors nicely, and that way should not require writing a page of boilerplate just to say 'my function might return an error from library foo or one from library bar or this error of its own'. (If error-chain is that one right way then it should be in the standard library and the documentation.) As it is it looks like 'this language isn't finished yet, come back in six months to see if it's any better' :-(

Re: My Struggles with Rust

#186

Use Nim

If you read the actual article, look at its horrifying Rust code examples, and see the author's conclusion to stick with Python, then "Use Nim" is EXACTLY the right response!

Saying "Use Nim - it's a statically typed compiled language that about equals Rust in performance, but has a much cleaner higher-level Python-flavored syntax, and a very Pythonic `parsecfg` module in stdlib" is only better for the RTFMably challenged readers who've never heard of Nim before.

Shame on the closed-minded pathological down-voters who've completely ruined HN...

Re: My Struggles with Rust

#187

It makes me sad to see the example. This is why I maintain `.unwrap()` is one of the worst things in rust. ...because people use it; and then say; 'but don't use unwrap...'; and then use it, and your 'safe' language then happily crashes and burns everytime something goes wrong. Blogs and documentation are particularly prone to it. Result and option types are good; but if you're gonna have unwrap, you basically have t…

Unwrap doesn't make rust unsafe, it's not a segfault. You also don't have to use the verbose match statement when using options and results; you either propogate the error with ?/try or you provide a default value, or you panic (if the error should not be happening).

Re: My Struggles with Rust

#188
post #177

Earlier quoted context omitted.

"new idioms and special syntax that is alien to pretty much everyone" -> "an `Error` trait with appropriate `From` impls." Note, that it may be entirely necessary for us to invent new idioms to make progress in the art of programming.

It might be worth pointing out that this Rust: #[derive(Debug)] enum ConfigError { Io(io::Error), Parse(ParseIntError), } impl From for ConfigError { fn from(err: io::Error) -> ConfigError { ConfigError::Io(err) } } impl From for ConfigError { fn from(err: ParseIntError) -> ConfigError { ConfigError::Parse(err) } } fn read_config() -> Result { Result::Ok(parse_int(read_config_file()?)?) } // given the following fn pa…

I would love a `#[derive(From)]` for newtype structs and enum variants. Would bring Rust error handling back below Java in boilerplate levels. :)

Re: My Struggles with Rust

#189

Earlier quoted context omitted.

Exceptions are the best way to handle errors. You can either handle them everywhere or ignore them and they'll rewind the stack. Unfortunately Rust and Go decided to use return values, instead of fixing problems with exceptions, which is step back, IMO.

I agree with you but until there is an empirical basis for our opinion-probably-honed-by-years-of-coding, these 2 languages will just continue to chug along without real exceptions My argument would be this: What is a runtime exception, really ? It's a state that the programmer did not handle (either due to lack of thoroughness or flaws in mental model). Suppose the error is just ignored: To this I ask, why would you…

I can't speak for Go, but what you describe isn't really a thing in Rust. Functions in Rust that might encounter an "exception" typically return something like `Result` where `T` is the type of what we hope we get and `E` is a type that encodes the details of the errors/exceptions we might see. It's an enum type that comes in two flavors: `Ok(T)` and `Err(E)`.

You can't just go happily along treating an `E` like it's a `T`, because the compiler won't allow it. So I'm not sure how you might get into a "corrupt state". You only have three choices: panic, return and pass the error back up the call stack, or handle it explicitly. All those choices short circuit what you were doing and don't leave you with anything that you could mistake for a valid result at any level of the call stack (again, this is enforced by the compiler).

Whether it's ergonomic is another question. I happen to like it, but for sure doing exceptions in Python means fewer LOCs, if that's what you're after. I tend to be more interested in how the features of a language help programmers to keep writing correct and maintainable code as the complexity of a project grows.

Post reply on HN