News for hackers. Stuff that matters.
Re:Rust 2019: Tame Complexity through Community Involvement ... by scaleout1 on Tuesday January 01, 2019 @01:56PM (#18801614)
Recently started introducing Rust in my team and here are my notes on some of the issues we have run into so far. We are primarily a Scala/JVM shop with a little bit of Python.

- `Error Handling` is a bit of a dumpster fire right now. Rust has something called a `Result` enum which is similar to `Either[Error,T]` monad in Scala however every single Rust crate I have used so far its creating its own `Error` enum which makes it really hard to compose `Result`. Ideally I would like to chain `Results` as `e1.and_then(e2).and_then(e3)` but its not possible due to incompatible error enums. Ended up using `https://docs.rs/crate/custom_error/1.3.0` to align the types.

- A lot of basic things are still influx and community wide standards have not been established. For example: I needed to externalize some environment specific settings in a Config but couldnt figure out where to put non-code assets in a cargo project and then how to reliable read them. In JVM world `src/main/resources` acts like a standard place for stuff like this but that patterns has not been established yet.

- Distributing code inside the company is hard because there is no integration with Artifactory or similar tools. We are directly referring git sha in Cargo right now and waiting for better solutions.

- Rust comes with a default unit test framework but it pretty bare bones. I havent seen examples of test fixture, setup/teardown support and loading test configs etc.

- I really like Rust compiler because of really good error messages it produces but its really slow and you start to notice it as you add more code/external crates

-IDE support is good but not great. I am using IntelliJ with Rust plugin as we use IntelliJ for Scala/JVM and it is nowhere as good as even Scala plugin which is pretty mediocre in itself.

Overall I am pretty happy with the language (except for Error issue) and most of my gripes are around the ecosystem and tooling around the language. Hopefully these will be resolved as language gains more momentum

Re:Rust 2019: Tame Complexity through Community Involvement ... by smaddox on Tuesday January 01, 2019 @03:24PM (#18802106)
You should try using `map_err` and the `?` operator, with your own error enum defined using the `failure_derive` crate, for example:

    foo().map_err(|e| MyError::FooError(e))?
      .bar().map_err(|e| MyError::BarError(e))?;
Re:Rust 2019: Tame Complexity through Community Involvement ... by kouteiheika on Tuesday January 01, 2019 @04:42PM (#18802568)
A tip - you usually don't need lambdas in such cases; this should work:

    foo().map_err(MyError::FooError)?
      .bar().map_err(MyError::BarError)?;
Re:Rust 2019: Tame Complexity through Community Involvement ... by smaddox on Tuesday January 01, 2019 @08:56PM (#18803671)
Oh, nice! I did not realize tuple constructors implemented `FnOnce`! Thanks for the tip!
Re:Rust 2019: Tame Complexity through Community Involvement ... by dakom on Tuesday January 01, 2019 @03:51PM (#18802273)
d'oh - you beat me to it ;)
Re:Rust 2019: Tame Complexity through Community Involvement ... by jgrowl on Tuesday January 01, 2019 @02:09PM (#18801664)
For error handling, check out failure if you haven't already: https://github.com/rust-lang-nursery/failure
Re:Rust 2019: Tame Complexity through Community Involvement ... by scaleout1 on Tuesday January 01, 2019 @02:23PM (#18801752)
Does it help with Error enum coming for third party crates? I can see how it will help with my own code though
Re:Rust 2019: Tame Complexity through Community Involvement ... by kouteiheika on Tuesday January 01, 2019 @02:11PM (#18801681)
> Rust comes with a default unit test framework but it pretty bare bones. I havent seen examples of test fixture, setup/teardown support and loading test configs etc.

This is one of the things that I really love about Rust - it has a testing framework that is so simple that it subtly pushes you to write better tests and better code.

What I mean by that is - it really wants you to write simple tests with no mocking and no convoluted state using simple asserts, and it really wants you to write code which is trivially testable. In other languages this might be problematic, but in Rust this synergises really well with other language features such as #[derive(...)], sum types and pattern matching. If you write good, idiomatic Rust code then all of those extra features of other testing frameworks are usually totally unnecessary.

Re:Rust 2019: Tame Complexity through Community Involvement ... by scaleout1 on Tuesday January 01, 2019 @02:22PM (#18801739)
I can see your point for unit testing but I am struggling to see how it will work for Integration testing. For example I would like to initialize a connection pool (or other heavy object) only once for the entire test, I .. havent figured out how to handle that except for creating a new one in each test. Any pointer on how you are handling those? Thanks
Re:Rust 2019: Tame Complexity through Community Involvement ... by est31 on Tuesday January 01, 2019 @03:52PM (#18802279)
I know a little about two of the big open source Rust projects, Servo and the Rust compiler.

For integration tests, both the Rust compiler as well as the Servo web engine have written their own test runners with an accompanying set of tools. This allows the test format to be as flexible as the target domain of the program. In fact, servo can just simply use the cross-browser wpt [1] test suite.

As for avoiding to initialize heavy objects, I only know about the Rust compiler's test runner initializing the entire compiler every time from zero for every single test file. The result is a quite long testing-time. Suggestions by me to use a shared driver were rejected by them because one test might affect the other one.

But in theory you could write your own custom test runner that has as much shared state as possible. You might also want to check out cucumber-rust [2] which I think allows for some way of sharing. You could also just group smaller tests into a small set of bigger tests where each group of tests shares some resource.

[1]: https://github.com/web-platform-tests/wpt [2]: https://github.com/bbqsrc/cucumber-rust

Re:Rust 2019: Tame Complexity through Community Involvement ... by kouteiheika on Tuesday January 01, 2019 @04:08PM (#18802373)
If it's something that doesn't take too much wall time I just simply initialize those from scratch for every test. If I have multiple tests which need some complex setup I just put that down in a separate function and call that in every test. If it's something reaaally expensive to initialize I just use lazy_static:

https://crates.io/crates/lazy_static

Re:Rust 2019: Tame Complexity through Community Involvement ... by staticassertion on Tuesday January 01, 2019 @03:49PM (#18802267)
Have you looked at stainless?
Re:Rust 2019: Tame Complexity through Community Involvement ... by dakom on Tuesday January 01, 2019 @03:50PM (#18802270)
> every single Rust crate I have used so far its creating its own `Error` enum which makes it really hard to compose `Result`.

Is there a reason why `map_err()` doesn't achieve what you need - i.e. to get everything into your error type no matter where it came from?

For example: OtherLibResult.map_err(|e| MyError::from(e))

Unless I'm mistaken - you ultimately are doing something like this in one form or another, because if you never match on the different Error variants that happened somewhere along the pipeline- then those details are being swallowed.

Implementing a From trait at least forces you to make that choice (all in one place too)- and then you can keep composing the results as needed since it's all in your MyError type

Re:Rust 2019: Tame Complexity through Community Involvement ... by scaleout1 on Tuesday January 01, 2019 @04:11PM (#18802395)
Yes thats the pattern I ended up using with the help of `custom_error` crate which automates the implementation of `From` for your custom errors. I guess the disconnect for me was that in Java/Scala all custom exceptions extend `Throwable` so types always line up, in rust custom Errors are disjointed so you have to wrap everything yourself to align the types.
Re:Rust 2019: Tame Complexity through Community Involvement ... by pornel on Tuesday January 01, 2019 @07:55PM (#18803434)
`Throwable` in Rust is `dyn std::error::Error`.

If you make your functions return `Box<dyn Error>` (or `failure::Error`), they will all convert out of the box. The `?` operator performs conversion itself. For chains `.map_err(From::from)` does it.

Re:Rust 2019: Tame Complexity through Community Involvement ... by epage on Tuesday January 01, 2019 @05:16PM (#18802749)
> - Rust comes with a default unit test framework but it pretty bare bones. I havent seen examples of test fixture, setup/teardown support and loading test configs etc.

There is an eRFC for better integration of custom test frameworks.

https://github.com/rust-lang/rfcs/pull/2318

Re:Rust 2019: Tame Complexity through Community Involvement ... by joelg236 on Tuesday January 01, 2019 @02:04PM (#18801642)
For non-code assets, do you know about `include_bytes`?
Re:Rust 2019: Tame Complexity through Community Involvement ... by tangent128 on Tuesday January 01, 2019 @08:46PM (#18803618)
I found rust-embed recently[0], which lets you embed an entire directory in release mode while deferring to the file system in debug mode.

[0]: https://github.com/pyros2097/rust-embed

Re:Rust 2019: Tame Complexity through Community Involvement ... by YorkshireSeason on Tuesday January 01, 2019 @02:28PM (#18801766)
Have you also evaluated Scala/Rust interop?

That would be an ideal combination for me: do performance critical stuff in Rust, use Scala everywhere else.