Live data from Hacker News

My Struggles with Rust

compileandrun.com

41–50 of 329 posts

Re: My Struggles with Rust

#41

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…

In an abstract sense, it's possible to offer more functionality without requiring much more friction. This is the premise of languages with good type inference, relative to earlier versions of C++/Java.

Though there might be more friction at some points, I imagine the Rust developers are taking these examples as good benchmarks for improvements.

EDIT: This exercise is very similar to the frustration when starting to use Haskell.

A lot of "simple" things feel more difficult because of the functional purity. But then you discover more patterns or libraries that help to handle this.

Design patterns surely exist for Rust that have yet to be discovered, but will turn out to properly encapsulate a lot of the difficulty (when combined with language improvements)

Re: My Struggles with Rust

#42

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 have run into something very similar in somebody else's python program where they declared a string that was later used (based on some conditionals) to locate a file. The thing is that most of the conditionals were never hit under ideal conditions (like passing all args etc.). It took me a lot of time to track down the bug (also because of the lack of awesome debuggers for Python).

Re: My Struggles with Rust

#43

  > import json
  > with open("config.json") as f:
  >  contents = f.read()
  > config = json.loads(contents)
translates to:

  extern crate serde_json as json;

  fn read_json() -> Result> {
      let file = std::fs::File::open("config.json")?;
      let config = json::from_reader(&file)?;
      Ok(config)
  }
And

  > import configparser
  > config = ConfigParser()
  > config.read("config.conf")
can be translated to:

  extern crate config;
  use config::{Config, File, FileFormat};

  fn read_config() -> Result> {
      let mut c = Config::new();
      c.merge(File::new("config", FileFormat::Json))?;
      Ok(c)
  }
Difficult stuff indeed.

Re: My Struggles with Rust

#44

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…

There are many ways to have both enforced error handling AND less boilerplate. Java's checked exceptions are much maligned but would work very well here. Another way would be having more syntactic sugar for Result-style monadic error handling, like the do notation in Haskell or for..yield in Scala.

Another issue raised by the original post is the fact that Rust has no top-level concrete error type that is convertible from all of the specific error types. This is also something that could be fixed without compromising other qualities of the Rust type system.

Re: My Struggles with Rust

#45

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…

C does not force you to check errors, as everything that could fail must return an error code and you could simply forget to check it. Python checks the error for you, as anything that could fail throws an exception, and so you have to go out of your way to actively write code to manage to override that check (with a try/except) to ignore the error.

Re: My Struggles with Rust

#46
post #8

Earlier quoted context omitted.

> It's probably because Rust looks and operates mostly like a high-level language, but still satisfies low-level constraints. In an ideal language, you could decide to ignore low-level constraints and your code would work just fine, although perhaps less efficiently.

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.

http://250bpm.com/blog:4

https://news.ycombinator.com/item?id=3953434

Re: My Struggles with Rust

#47
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…

> 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 when the writer learned the language.

As someone that participated in that conversation, I think that's a pretty inaccurate characterization of it. It's not about when the writer learned the language, but rather, what problem you're trying to solve. If you'll allow me to summarize very briefly (perhaps at the expense of 100% accurary):

    * Use unwrap/expect when you don't care.
    * Use `try!`/`?` with Box in simple CLI applications.
    * Use `try!`/`?` with a custom error type and From impls in libraries.
    * Use combinators (e.g., map_err) when you need more explicit control.
You might imagine that you could use any number of these strategies depending on what you're trying to do, which might range from "a short script for personal use" to "production grade reliability."

All of this stuff was available at Rust 1.0. (Except for `?`, which is today an alias to `try!`.) It all falls out of the same fundamental building blocks: an `Error` trait with appropriate `From` impls.

The one exception to this is that, recently, there has been a surge in use of crates like error-chain to cut down on the code you need to write for defining custom error types and their corresponding `From` impls. But it's still all built on the same fundamental building blocks.

Re: My Struggles with Rust

#48
post #31

Hello Justin Turpin! Sorry to hear your struggles with rust. It's always going to be a bit more verbose using rust than Python due to type information, but I think there are some things we could do to simplify your code. Would you be comfortable posting the 20 line code for us to review? I didn't see a link in your post. Anyway, so some things that could make your script easier: * for simple scripts I tend to use the…

Was the expect method​ supposed to be named except, as in exception? That would make a lot more sense.

Re: My Struggles with Rust

#49

Earlier quoted context omitted.

I find result types to be much easier to understand and work with than exceptions. Result types can be handled by the type system, even when you have checked exceptions in java, there are still exceptions that aren't checked, and the syntax for the checking becomes monstrous.

The anti-pattern I've seen in dysfunctional enterprise development shops (i.e. most of them) is that checked exceptions mean exceptions that "we'll never have" get buried lower in the stack; so code can fail silently and continue running just to avoid the monstrous checking code and propagation of exception type declarations up the call stack. I don't see how the Rust approach would avoid this fate but I doubt it wil…

I think that part of it is that the idea of a Result type being normal will help prevent much of the cruft and burying we see with exceptions. I also feel like handling Result types is more natural than exceptions.

First, you _have_ to do it, even if that means a try! and passing the buck. The syntax for this isn't as monstrous as it is for checked exceptions as well.

Second, it feels more like a natural code-flow, not the break that exceptions provide.

Third, it's useful for more than just "Exceptions". Coupled with Optional types, it provides a more expressive way of not-hapy-path-code where exceptions just feel heavy handed. For instance https://docs.python.org/3/library/stdtypes.html

     d[key]

        Return the item of d with key key. Raises a KeyError if key is not in the map.
The key not existing isn't really exceptional. A proper optional, union, or result type handles this case much more easily.

In sum, I think the expressibility of Result and Option, along with a more natural flow for handling them will make working around them less tempting/viable/easy to pass over in a code review.

Re: My Struggles with Rust

#50
post #44

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…

There are many ways to have both enforced error handling AND less boilerplate. Java's checked exceptions are much maligned but would work very well here. Another way would be having more syntactic sugar for Result-style monadic error handling, like the do notation in Haskell or for..yield in Scala. Another issue raised by the original post is the fact that Rust has no top-level concrete error type that is convertible…

> Another way would be having more syntactic sugar for Result-style monadic error handling [...] Another issue raised by the original post is the fact that Rust has no top-level concrete error type that is convertible from all of the specific error types.

Well... Rust has both. Any type that satisfies the `Error` trait can be converted to `Box`, which is provided by the `impl Error for Box` and `impl From for Box` impls. And it so happens that this conversion can be done for you automatically using Rust's `?`. For example, this:

    use std::error::Error;
    use std::fs::File;
    use std::io::{self, Read};

    fn main() {
        match example_explicit() {
            Ok(data) => println!("{}", data),
            Err(err) => println!("{}", err),
        }
    }

    fn example_explicit() -> Result> {
        let file = match File::open("/foo/bar/baz") {
            Ok(file) => file,
            Err(err) => return Err(From::from(err)),
        };
        let mut rdr = io::BufReader::new(file);

        let mut data = String::new();
        if let Err(err) = rdr.read_to_string(&mut data) {
            return Err(From::from(err));
        }
        Ok(data)
    }

can be written as this without any fuss:

    use std::error::Error;
    use std::fs::File;
    use std::io::{self, Read};
    
    fn main() {
        match example_sugar() {
            Ok(data) => println!("{}", data),
            Err(err) => println!("{}", err),
        }
    }
    
    fn example_sugar() -> Result> {
        let file = File::open("/foo/bar/baz")?;
        let mut rdr = io::BufReader::new(file);
        
        let mut data = String::new();
        rdr.read_to_string(&mut data)?;
        Ok(data)
    }
The problem is that the conversion to `Box` winds up making it harder for callers to inspect the underlying error if they want to. This is why this approach doesn't work well in libraries, but for simple CLI applications, it's Just Fine.
Post reply on HN