
My Struggles with Rust - wkornewald
https://compileandrun.com/stuggles-with-rust.html
======
kstenerud
One thing I've found with rust is that you struggle struggle struggle trying
to do a simple task, and then finally someone says "Oh, all you need to do is
this".

Rust has already reached the point where it leaves the world behind. Only the
people who have been there since the early days really understand it, and
getting into rust gets harder and harder as time goes on.

Yes, there's some awesome documentation, and the error messaging has gotten a
lot better. But the power and flexibility of Rust comes at the cost of it
becoming harder and harder to figure out how all the thousands of little
pieces are supposed to fit together. What's really needed is a kind of
"cookbook" documentation that has things like "How to read a text file with
proper error handling" and "what is the proper way to pass certain kinds of
data around and why".

Right now there's a lot of "what" documentation going around, but little that
discusses the "how to and why".

~~~
std_throwaway
How is this problem solved for C++?

~~~
hellofunk
The nice thing about C++ is that it is so widely used that for just about any
task you can find examples of how to do it (usually many different ways to do
something), especially for tasks like this article. You will also find plenty
of C example, which you could also use in C++.

~~~
arcticbull
Unfortunately 90% of them will be outdated/wrong/unsafe. Being backward
compatible with 30 years of code means you're unlikely to find the right
answer for the present. And worse, you won't know it.

~~~
lossolo
You base your 90% claim based on what? I have entirely different experience
and I am using C++14 and C. K&R "C programming language" is from 1978 and it's
still one of the best books about C still used in 2017. I see many
stackoverflow answers updated from C++98 to C++11/14 and some even to C++17.

~~~
kibwen
As someone whose first introduction to C was through K&R, and still has that
torn and tattered copy on his bookshelf, the idea that we're still
recommending K&R as "one of the best books about C" only validates
arcticbull's (sadly-downvoted) point.

~~~
prewett
K&R is so clearly written that I was able to understand it as a sixth grader.
(At least, I felt like I did; subsequent events later proved that I did not
understand pointers, but I don't think it was K&R's fault that a sixth grader
didn't understand.) That's why it's still being recommended and the much
thicker tomes also purporting to teach the C language have been forgotten.
It's probably the same reason that your copy is torn and tattered.

~~~
kibwen
_> It's probably the same reason that your copy is torn and tattered._

Er, well I was also using it as a mousepad for about three years...

------
erickt
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 `.expect` method if I plan on killing the program if there is an error. It's just like unwrap, but it will print out a custom error message. So you could write something like this to get a file:
    
    
        let mut file = File::open("conf.json")
            .expect("could not open file");
    

(Aside: I never liked the method name `expect` for this, but is too late to do
anything about that now).

* next, you don't have to create a struct for serde if you don't want to. serde_derive is definitely cool and magical, but it can be too magical for one off scripts. Instead you could use serde_jaon::Value [0], which is roughly equivalent to when python's json parser would produce. * next, serde_json has a function called from from_reader [1], which you can use to parse directly from a `Read` type. So combined with Value you would get:
    
    
        let config: Value = serde::from_reader(file)
            .expect("config has invalid json");
    

* Next you could get the config values out with some methods on Value:
    
    
        let jenkins_server = config.get("jenkins_server")
            .expect("jenkins_server key not in config")
            .as_str()
            .expect("jenkins_server key is not a string");
    

There might be some other things we could simplify. Just let us know how to
help.

[0]:
[https://docs.serde.rs/serde_json/enum.Value.html](https://docs.serde.rs/serde_json/enum.Value.html)

[1]
[https://docs.serde.rs/serde_json/de/fn.from_reader.html](https://docs.serde.rs/serde_json/de/fn.from_reader.html)

~~~
eriknstr
While it's too late to change the name "expect", could one create an alias for
it and call it say, "on_error"?

~~~
eximius
I'd expect 'on_error' to take a function as a callback, not a string. But yes,
a better named function could be added

~~~
skybrian
Perhaps or_die, similar to Perl?

~~~
sanderjd
Yep, this is what I always wanted it to be! But maybe or_panic would be
better. Naming things is hard.

------
dbattaglia
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 use
as Python. Maybe I'm wrong.

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

At this point the cognitive load required to read and understand Rust
implementations of "typical" practical problems is _rather higher_ than it is
for C++. And it seems to be getting steadily worse from my perspective on the
outside.

~~~
burntsushi
> 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<Error> 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.

~~~
jackmott
"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.

~~~
twic
It might be worth pointing out that this Rust:

    
    
        #[derive(Debug)]
        enum ConfigError {
            Io(io::Error),
            Parse(ParseIntError),
        }
        
        impl From<io::Error> for ConfigError {
            fn from(err: io::Error) -> ConfigError {
                ConfigError::Io(err)
            }
        }
        
        impl From<ParseIntError> for ConfigError {
            fn from(err: ParseIntError) -> ConfigError {
                ConfigError::Parse(err)
            }
        }
        
        fn read_config() -> Result<i32, ConfigError> {
            Result::Ok(parse_int(read_config_file()?)?)
        }
        
        // given the following
        fn parse_int(str: String) -> Result<i32, ParseIntError> { ... }
        fn read_config_file() -> Result<String, io::Error> { ... }
    

Is, in a sense, the equivalent of this Java:

    
    
        int readConfig() throws IOException, ParseException {
          return parseInt(readConfigFile());
        }
        
        // given the following
        int parseInt(String str) throws ParseException { ... }
        String readConfigFile() throws IOException { ... }
    

The reason i say that is that this:

    
    
        throws IOException, ParseException
    

Is essentially a sum type. It says that if this method results in a failure
value, it can fail with one of two types of failure values. It might not look
like a new type, because in Java, types are almost always nominal, and this is
structural, but that's what it is. I think that throw and catch clauses are
the only place that Java will let you define an ad-hoc sum type. You have to
use polymorphism everywhere else you want a variety of types.

Whereas in Rust, there are no structural sum types, and so the only way to
make something resembling a sum type is:

    
    
        enum ConfigError {
            Io(io::Error),
            Parse(ParseIntError),
        }
    

Which means you also have to write the machinery to convert between the types.

I wonder if it would help to have a compiler- or library-defined From impl for
all newtype enum variants (or all newtype structs more generally), that makes
the variant from its argument. Or maybe it could be derived. It would wipe out
a lot of this boilerplate.

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

~~~
killercup
There are some crates that add a derive for errors, but most people tend to
use error-chain or quick-error. Both give you the boilerplate pretty much for
free, with various other advantages. E.g., using error-chain, you can just
write the above code as

    
    
        error_chain! {
            foreign_links {
                Io(io::Error);
                Parse(ParseIntError);
            }
        }
    

and it'll even generate an aliased `Result` type as well as fancy chaining
support.

~~~
Rusky
Right- error_chain is just a bit too much magic for me compared to what
#[derive(From)] would do.

------
tinco
Being able to port a 20 line Python script to a 20 line Rust is the holy
grail. Surely Rust has the ambition to one day achieve that, but it is by no
means the main priority nor the original design goal of the language.

Justin criticizes the file_double function, it being complex with nested maps
and conditionals. All of this complexity is also in the Python code, just
hidden away in abstractions, the library and the virtual machine. Rust, right
now, is still very explicit and revealing of inherent complexities. This code
is exactly why you should use Python and not Rust for this kind of little
script. One day the Rust developers hope Rust will be comfortable enough for
you to consider using Rust in this situation, but it won't be soon.

The point gets softened a little by the remark that it probably would not be a
picnic in C either, but I don't think even that is true. C still allows you to
be very expressive, it would not encourage using those maps or even half of
those conditionals. Rust is just that more explicit about complexity.

That said I honestly believe Rust is the best thing that has happened to
programming languages in general in 20 years. Rust is rocking the socks off
all the non-web, non-sysadmin fields, soon its community will make good
implementations of almost every hard problem in software and Rust will be
absolutely everywhere.

~~~
chillingeffect
Rust hasn't made significant incursions into math modelling or industrial
processing. It's advantages are slim there. Embedded will fracture into
network interfacing and realtime where user input is less hostile, more
predictable and doesn't require extensive constraint.

~~~
e12e
Isn't it this kind of thinking that leads to sql injections in number plate
readers?

------
f1b37cc0

      > 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<json::Value, Box<std::error::Error>> {
          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<Config, Box<std::error::Error>> {
          let mut c = Config::new();
          c.merge(File::new("config", FileFormat::Json))?;
          Ok(c)
      }
    

Difficult stuff indeed.

~~~
stable-point
I think this highlights that while there are easy solutions to the problem the
OP faced, they're difficult for a newcomer to discover. (I have been using
Rust for a couple of months and I'd also have reached for serde and maybe
serde_derive to solve the problem).

Hopefully this is something the Libz Blitz[0] will solve with their Rust
Cookbook[1]. (You could almost but not quite arrive at as simple a solution
from chapters 1 and 2).

[0] [https://blog.rust-lang.org/2017/05/05/libz-blitz.html](https://blog.rust-
lang.org/2017/05/05/libz-blitz.html)

[1] [https://brson.github.io/rust-
cookbook/intro.html](https://brson.github.io/rust-cookbook/intro.html)

~~~
leshow
The curious thing is that OP linked to the error handling docs, and the
solution with Box<Error> is right there.

------
pornel
These struggles are real. I don't see a way around them other than just
learning them (and then they go away, because you know what code won't work,
and don't fight it).

It's probably because Rust looks and operates mostly like a high-level
language, but still satisfies low-level constraints.

e.g. the confusing difference between `&str` and `String` is equivalent of C's
`const char * str = ""` vs `char * String = malloc()`.

In C if you had a code that does:

    
    
         char *str = foo();
         free(str);
    

you'd know that in `foo()` you can't return `"error"`, since an attempt to
free it would crash the program. And the other way, if the caller did not free
it, you'd know you can't have a dynamic string, because it would be leaked. In
Rust you don't see the `free()`, so the distinction between non-freed `&str`
and freed `String` may seem arbitrary.

~~~
rvense
These struggles are indeed real, but at least as far as verbose error handling
goes, remember that Rust is forcing you to handle a lot of things that are
silently ignored in Python. Truly equivalent Python code would include a bunch
of exception handling and checks for nil.

~~~
nathan_f77
I haven't done much Rust, but why isn't there a safe way to do some of this
implicitly? If you don't want to think about errors and just want to crash,
then I feel like there should be a sane way to crash the script with a default
error message. Could you write a thin abstraction to achieve this? Maybe there
could be a new crate called "rust-script" or something, where the goal is to
write code as easily as Python or Ruby.

Again, I'm not a Rust developer, but it's not hard to imagine an abstraction
(or even a transpiler) that makes it easy to read a file, parse it as JSON,
and do something with the data.

~~~
rvense
As the other comment says, that's exactly what's done with unwrap and try. You
do obviously have to write that out, but on the other hand that means you know
where to add the error handling when you come back after the fact. It might
take a little getting used to, but it's a decent compromise. I'd say it's a
matter of taste, but yeah, there are a few more characters there.

~~~
MichaelGG
Couldn't there be an opt-in Deref for Option and Result? Then you could just
pretend everything unwrapped fine.

------
Inufu
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...](https://doc.rust-
lang.org/book/error-handling.html#the-limits-of-combinators)

In practice, I find a type like Google's util::StatusOr
([https://github.com/google/lmctfy/blob/master/util/task/statu...](https://github.com/google/lmctfy/blob/master/util/task/statusor.h))
a lot easier to use (I've written >100kloc c++ using it). This uses a
standardized set of error codes and a freeform string to indicate errors. I've
yet to encounter a case where these ~15 codes were insufficient:
[https://github.com/google/lmctfy/blob/master/util/task/codes...](https://github.com/google/lmctfy/blob/master/util/task/codes.proto)

~~~
Matthias247
I think having Result alone does not make error handling complicated, but
having different error types for each operation (and concrete result) instead
of using one generic error type for all of them does by pushing the job of
unifying erros towards the user.

Go works around the problem by Error being an interface, which means any
function can return any kind of error without needing to transform it to
another form. However Go benefits from the Garbage Collector here - I totally
understand why Rust libraries don't want to return heap allocated errors.

Maybe C++ std::error_code/error_condition provides some kind of middle ground:
It should not require a dynamic allocation. And yet the framework can be
expanded: Different libraries can create their own error codes (categories),
and error_codes from different libraries can all be handled in the same way:
No need for a function that handles multiple error sources to convert the
error_codes into another type.

The downside is that the size of the error structure is really fixed and
there's no space to add custom error fields to it for error conditions that
might require it. A custom error type in Result<ResultType,ErrorType> can be
as big or small as one needs.

~~~
pcwalton
> However Go benefits from the Garbage Collector here - I totally understand
> why Rust libraries don't want to return heap allocated errors.

This doesn't have to do with the GC or lack thereof. Instead it's part of the
philosophy of zero-cost abstractions: idiomatic C libraries don't require heap
allocations to return errors, so neither does Rust.

------
msangi
A common theme I find in posts criticizing Rust is that their authors take a
problem they've already solved in another language, try to blindly convert it
in non-idiomatic Rust and then complain because things get awkward.

I think that it's important to pick the right tool for the job and to follow
the patterns of the tool you're using.

Is Rust the right tool for the task described in the post? Probably not, but
it could still be used albeit it will always require more work than Python.

What's really missing is a resource showing common problems and their
idiomatic solutions.

------
thegeomaster
The `error-chain` crate [1] exists to get rid of precisely the error handling
boilerplate the author has encountered. That's not ideal, though, as I believe
that a place for such functionality is in the core language, not a separate
library, but it gets the job done. As for the `let mut file` bit, that makes
sense to me: a file in the standard library is an abstraction over a file
descriptor in the operating system, and the descriptor has a file pointer
which must be advanced when you read from it. I don't consider it internal
state; the read operation will return new data every time, so it's not a pure
function. It follows that in order to behave that way, it has to depend on
some pretty explicit state.

As the other comment said, Rust needs to make some trade-offs, because you
simply can't have an expressive and easy-to-use language that runs so close to
the metal and is aimed at being C++-level fast. As such, Rust will never be as
easy to write as Python, and for scripts like the author mentioned, I'd say
that Python is a much better choice than Rust.

Rust is, by design, a systems programming language and it does have
complexities and gotchas that arise from the need to have a lot of control of
what actually happens at the machine code level. If we had a Sufficiently
Smart Compiler(tm), of course, you wouldn't have to worry yourself about those
low-level details and just write what your program needs to do and nothing
more. However, in the absence of such an ideal, we must accept that a high-
level abstraction must always leak in _some_ way in order to let us control
its operation more closely to get the performance we need. In my opinion, it's
much better that necessary abstraction leakage is made a deliberate part of
the API/language and carefully designed to minimize programmer error, and
Rust, I think, does a good job of doing exactly that.

That's not to say that the language cannot be made more ergonomic. For one, I
think that rules for lifetime elision are a bit too conservative and that the
compiler can be made smart enough to deduce more than it currently does. I'm
also excited about the ergonomics initiative, and I hope that the core team
will deliver on their promises. In general, as someone who's written more
lines in C/C++ in my life than any other language, I'm very excited about the
language as a whole, as I think it provides the missing link between those
languages that are expressive, high-level, and reasonably safe but slow, and
those that are fast, low-level, a bit terse, and allow one to shoot oneself in
the foot easily.

[1]: [https://crates.io/crates/error-chain](https://crates.io/crates/error-
chain)

~~~
JoshTriplett
I'd like to see error-chain standardized into the standard library as well. It
seems by far the most sensible approach to building Error types.

~~~
shepmaster
Conversely, I __really hope it doesn 't __. error-chain doesn 't implement
`PartialEq` which means that I can no longer write tests for my failure types.
I'm a big believer in quick-error instead.

~~~
JoshTriplett
There are some good `assert_match` macros that allow you to pattern-match
error cases instead.

Might also be possible for error-chain to implement PartialEq if you didn't
want some of its generalized error-boxing bits. If that's something you need,
you might file an issue.

------
cdunn2001
[https://nim-lang.org/docs/parsecfg.html](https://nim-
lang.org/docs/parsecfg.html)

(Scroll to the examples.)

An exception would be thrown on error. That exception could be trapped in a
simple try/catch block.

Nim is very similar to Python -- but statically typed and compiled (quickly)
to machine code. There are many situations where Python is a better choice
than Nim, but if you're looking to translate Python code for speed and type-
safety, Nim is worth considering.

And if you want to translate Python to Nim gradually, look at this magic:

For calling Nim from Python: * [https://github.com/jboy/nim-
pymod](https://github.com/jboy/nim-pymod)

For calling Python from Nim: * [https://github.com/nim-
lang/python/tree/master/examples](https://github.com/nim-
lang/python/tree/master/examples)

------
dep_b
The big question is would the Python script crash or handle the error when
obvious problems like not valid JSON or file not found happen?

My experience with Swift vs Objective-C is that clean Swift is crash free but
more verbose when all other things are equal.

If you don't need that level of security because it's just a small script
Python was the right choice.

~~~
scriptkiddy
Depending on where it crashed, the Python script would raise an exception. It
would most likely be an `IoError`, `KeyError`, or `ValueError`. Then it would
show an error message with a line number, column number, and traceback. Using
a debugger would allow you to step backwards through the traceback to
determine if the error was caused by something further up the line or where
the exception was raised.

All of Python's exceptions are an instance of `Exception`. In order to catch
and handle any exception that can be raised, you can use a `try, except` block
with the base `Exception` class. This, however, is bad practice as there may
be some exceptions you want to ignore and, generally, you also want to print a
different error message depending on which exception was raised.

~~~
dep_b
So the Python script proposed in this article really just skips all error
checking and will die just the same as the hard unwrapped Rust version with
the only benefit it actually produces a more user-friendly error and it
doesn't look as ugly.

I can't edit my reply anymore but the question was actually meant to be
rhetorical rather than I really wanted an answer.

~~~
scriptkiddy
> So the Python script proposed in this article really just skips all error
> checking

Only if the programmer chooses to not handle exceptions, which is bad
practice.

>and will die just the same as the hard unwrapped Rust version with the only
benefit it actually produces a more user-friendly error and it doesn't look as
ugly.

No, it won't die unless the programmer wants it to. In Python you can catch an
exception and continue the operation in a different manner. In fact, it is
common practice in a lot of Python libraries to use exceptions to determine
the presence of data and act according to whether or not the exception
occurred. If I'm not mistaken, I believe that this is common practice in most
languages that utilize the exception pattern.

> I can't edit my reply anymore but the question was actually meant to be
> rhetorical rather than I really wanted an answer.

Well, you seem to not know a lot about exception handling in Python, so I hope
I was at least a little helpful.

------
cousin_it
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.

~~~
jimktrains2
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.

~~~
jeremyjh
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 will
ever be used in these contexts to begin with.

~~~
jimktrains2
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](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.

~~~
jeremyjh
You don't have to convince me of the value of Result types, I prefer them too.
I've only written a couple thousands of lines of Rust but tens of thousands of
Haskell which rely on the same concepts (though its nicer with do notation). I
don't have faith in the masses though and right now there is a strong
selection bias in Rust that means only people concerned with quality and
correctness are using it in the first place.

I think twenty-five years ago there were similar hopes and dreams for Java
checked exceptions.

~~~
int_19h
The big problem with Java checked exceptions was that the language was simply
not flexible enough in other areas to handle higher-order stuff. For example,
quite often you want to define something like "this method takes object X, and
throws everything that X.foo() can throw, and also E". But there's no way to
express it in Java. So the moment you start doing any sort of HOF-like stuff -
even as simple as event handlers - you have to struggle with checked
exceptions.

This is not a problem in Rust.

------
djhworld
The author doesn't really justify why he needed to port the python script to
rust in the first place.

Pulling down some JSON, doing a bit of transformation and sending alerts seems
like a perfect candidate for a high level language, I don't see any reason why
you would port it to Rust unless you had significant performance concerns

~~~
burntsushi
> The author doesn't really justify why he needed to port the python script to
> rust in the first place.

And they don't need to. When I first learned Rust, I tried to write a `filter`
function. Why would I ever do that? I could write `filter` much easier in
Python, or heck, just use the `filter` method on iterators that is already in
the standard library. I did it because I saw it as an opportunity to learn. I
wanted to connect something I knew (`filter`) with something I didn't know
(Rust).

~~~
djhworld
I guess I was getting at the fact that porting the python script that does
something very simple is the wrong tool for the job.

A good candidate for trying to learn Rust is to find a script or tool that
currently has the problems that Rust claims to fix.

~~~
burntsushi
When someone comes to me and says, "I just spent the weekend porting a simple
Python web crawler that was working just fine to Rust, and I came across
problems x, y and z." What do you think an appropriate response is? Should we
berate them for "choosing the wrong tool for the job"? Or should we ask them
what they're problems were and help them fix them?

I'm not saying we shouldn't have a conversation about which-tools-are-
appropriate-when, but when someone is obviously trying to learn, let's put
that on the back burner.

------
ungzd
I don't think it should be as easy and concise as python — it's systems
programming language without GC. It is not designed to be used in place of all
programming languages, just in place of C and C++.

------
liveoneggs
Isn't nim built for pythonists to do just this sort of thing?

~~~
mikebenfield
I think Nim may be the most underdocumented project I've ever used. I spent a
week or so with it, but quickly grew extremely frustrated as I was constantly
scouring old forum posts to learn how to use the basic features of the
language. That is not a tolerable situation for me.

~~~
Recursing
I think the idea has great potential, but the absence of a strong community is
killing it :(

~~~
cdunn2001
Nah, I've always found quick answers to questions in the forum.

Simplicity is under-rated. With Nim, most problems are easy to grasp just by
reading source code. And when I don't understand the docs, I look for a simple
example at [http://rosettacode.org/](http://rosettacode.org/)

------
kibwen
See also previous discussion on /r/rust:
[https://www.reddit.com/r/rust/comments/69i105/the_grass_is_a...](https://www.reddit.com/r/rust/comments/69i105/the_grass_is_always_greener_my_struggles_with_rust/)

------
gavanwoolery
Rust has stressed ergonomics of late, yet I sometimes struggle to read Rust
code. Obviously, there is value in elegant code, but my question is, would
anybody find value in an extremely simple language that could compete with the
likes of c/c++? Does something like this exist?

~~~
kevin_thibedeau
Nim. It lets you get stuff done without having to drink the provably correct
kool-aid.

~~~
_pmf_
GC, though, an a much, much smaller community.

~~~
gavanwoolery
googling "GC language" predictably comes up with mostly links to garbage
collection. Do you have a link to this language?

~~~
Varriount
There's the Nim website[0] as well as the document describing the (soft) real-
time GC that Nim comes with[1].

[0] [https://nim-lang.org](https://nim-lang.org) [1] [https://nim-
lang.org/docs/gc.html](https://nim-lang.org/docs/gc.html)

~~~
gavanwoolery
oops, I was confused - thought the comment was talking about a language named
GC, rather than the fact that Nim has GC.

------
shadowmint
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 to have exceptions as well (or some kind of panic recovery),
because, people prefer to use it than use the verbose match statement. :/

~~~
jjnoakes
> 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 have to have exceptions as well

Why do you think that? unwrap() is meant to be the same as throwing an
uncatchable exception; if you want to throw an exception that you mean to
catch somewhere, you should be using something else.

> people prefer to use [unwrap()] than use the verbose match statement

People may not be aware (which will come with time) but there are more than
just those two choices when it comes to error handling in Rust.

~~~
shadowmint
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? I've never seen a meaningful
justification for it other than not wanting to handle errors properly.

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

~~~
steveklabnik
> I didnt say it was unsafe, I said it crashes.

Your phrasing implied that you said the crash was not safe, as you put
"unsafe" in quotes and contrasted it with the crash. At least, that's what I
understood you to be saying too.

~~~
shadowmint
meh.

I feel like any time someone mentions the word 'safe' regardless of context,
the rust safety pedants roll out of the woodwork to dispute to dispute any
minute detail of what's been said, regardless of if its relevant to the
discussion at hand. I shouldn't have put 'safe' in the comment at all, what a
waste of a thread.

My point had nothing to do with safety; it was purely that having given advice
being to write good code that doesn't panic, and then having code that
shamelessly panics in all your examples is hypocritical.

`?` is a better choice in basically every case; I'm glad to see the
documentation will be moving eventually towards using that.

~~~
jjnoakes
In the context of this article and discussion, panics via unwrap (or better,
expect) is a fine answer, and I'm not sure why you think it is hypocritical to
suggest it, especially to Rust newcomers who are looking to write script-like
programs.

Panics mostly match the verbosity, ergonomics, and functionality of the
analogous python script.

Now I agree that a caveat should follow advice like using unwrap and expect,
perhaps a small blurb about how they should eschewed for better error handling
when you want to catch the errors and make decisions because of them
(especially when writing libraries) but that's quite a bit short of
hypocritical to me.

~~~
shadowmint
> a caveat should follow advice like using unwrap and expect, perhaps a small
> blurb about how they should eschewed for better error handling when you want
> to catch the errors and make decisions because of them.

That is literally the definition of hypocrisy; the behavior of people who do
things that they tell other people not to do.

"When you do this, do it like this, but properly with error handling." :P

Anyhow, as I said its my oppinion that unwrap() is lazy, and `?`, `expect` and
`assert!` cover the same functionality in more explicit and meaningful way.

You're welcome to your own opinion.

~~~
jjnoakes
> That is literally the definition of hypocrisy; the behavior of people who do
> things that they tell other people not to do

Except I'm not saying that at all.

I said it's fine to use unwrap() or expect() if you want script-like default
behavior (a developer-centric error message and a quick exit with a bad return
code) and if you want something more than that, then use something better than
unwrap() or expect().

There's no hypocrisy here. I think anyone should follow that advice. Me, you,
a newb to Rust, a Rust veteran, anyone. Same advice.

------
ricardobeat
If the goal is [fast, compiled, statically typed], a language like Crystal [1]
would probably be a better fit for the author:

    
    
        require "yaml"
        config = YAML.parse(File.open("test.yaml"))
    

[1] [http://crystal-lang.org](http://crystal-lang.org)

~~~
brobinson
Does Crystal have YAML.load_file like Ruby does? Loading a YAML file only
requires one method call in Ruby.

~~~
ricardobeat
Crystal does async IO underneath, so this approach allows the use of a
streaming parser (you're just passing it a file handle, not the contents).

~~~
brobinson
Cool, thanks!

------
alkonaut
Would it be theoretically possible to get to this?

    
    
        fn gimme_config(some_filename: &str) -> MyConfiguration
        {
           toml::from_file(some_filename).unwrap()
        }

------
vultour
This is a staggeringly bad comparison, almost like comparing drawing a line in
C# (couple lines) to trying the same thing in C++ (possibly 100+ lines).

------
AlphaWeaver
Why was the title of this post changed?

------
Dowwie
Fortunately, Rust gets so much love from so many that it will thrive without
winning over Everyone

------
frik
Rust is important, it has a great chance being a first modern native system
language (memory safety).

Though, I wish it has less exotic syntax. It's like C++ and Erlang had a baby.
Look at modern languages with nice syntax like Go, Julia, Swift and compare it
to Rust. Someone coming from C, C++, C#, Java, PHP and JavaScript has to learn
a lot of new syntax twists that look uncommon and different for little reason.
Sure some overly complex early syntax ideas like different ones for different
pointer types vanished in newer Rust releases. Now it's probably too late to
improve the syntax.

~~~
hsivonen
It continues to puzzle me when people think of Rust's syntax as particularly
exotic.

It's much closer to C/Java/JavaScript than e.g. Python or Bash are. Rust still
has curly braces for blocks, uses ampersand and asterisk in ways that aren't
too far from C, uses dot in a way that's not too far from C or Java and uses
less-than and greater-than to denote generics like C++ and Java.

Personally, what keeps tripping me up when moving between languages is either
forgetting to put parentheses around "if" conditions in non-Rust languages
after writing Rust or having the Rust compiler complain to me about
unnecessary parentheses after writing non-Rust code.

But if new languages couldn't do things like omit unnecessary parentheses
around the "if" condition or improve readability by moving the return type to
come after the function name, that would seem like too big of a restriction on
trying to make some syntactic progress.

Edit: Plus it makes sense to have types after the variable name when they are
optional in most cases (and then to have them in the same order in function
signatures for consistency).

~~~
twic
> Edit: Plus it makes sense to have types after the variable name when they
> are optional in most cases (and then to have them in the same order in
> function signatures for consistency).

Since you brought this up, the one thing that really (but irrationally!) winds
me up about Rust's syntax is that variables are:

    
    
      let foo: Bar = ... ;
    

And functions are:

    
    
      fn foo() -> Bar { ... }
    

Why not use a colon for the return type of a function as well?

~~~
jomohke
"returns" and "is of type" are different concepts, so having different syntax
for them makes sense.

\- In your first example, foo is of type "Bar"

\- In your second, foo is of type "function that returns Bar".

ie, if you stored your foos in separate variables they would be:

    
    
        let my_var: Bar = ...
    

vs

    
    
        let my_function: fn() -> Bar = ...
    

If they were to change it to your suggestion the latter would be either
awkward (multiple colons?) or inconsistent (different syntax for types and
declarations).

------
p0nce
Hopefully some mechanism to handle exceptional cases at the language level
will be invented soon.

------
vfclists
Use Nim

~~~
Symmetry
The author was using this as an opportunity to learn Rust so while it might
look sort of crazy to use a systems programming language for build failure
notification there was a reason behind their decision. Nim is a lovely
language but in the author's case he doesn't really care about speed for the
use case so if they were being strictly pragmatic they could have just stuck
with Python.

~~~
vfclists
What!! Just as I was responding another of my hard earned points got deducted

Who are these mean Rustaceans?

Is Nim considered such a threat to Rust?

~~~
SyrupThinker
I guess people consider your comment not really helpful. Everyone could come
into this thread and write "Use <favorite language here>".

If you'd have provided some good advantages of Nim in this case, or in general
added to the discussion at hand, you might have gotten less downvotes.

Maybe you want to stop attacking a community directly, aswell... last time I
checked this site wasn't Rustacean only.

------
vfclists
The author might consider Nim - [https://nim-lang.org/](https://nim-
lang.org/). It is 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.

PS. To my earlier downvoters can I have my hard won karma back, please??? This
is the response I have been advised to proffer after consulting on the Nim
forum, after my earlier terse comment.

~~~
yongjik
If you're so concerned of imaginary internet points (a.k.a. hard won karma),
I'd advise you to stop talking about that. Complaining about downvotes is one
of the few reliable ways in HN to get further downvotes. (I mean, you could
say "What? That's bollocks!" and stop caring about votes given by these
mindless hordes. Or you could follow the social norm and happily gather sweet
internet karma. It's up to you.)

