/rust-notes

Personal notes while learning Rust. Mainly documenting pain points along the way.

Rust Bumps

This is a collection of notes I have on sharp edges I've encountered in my journey to learn Rust, in hopes that I can use them in the future to target contributions to Rust proper, or to share with others who might be interested.

Contribution Process

It wasn't immediately clear what the process of actually contributing to Rust proper is.

Solutions

I went to #beginners in the Rust discourse, and they pointed me at the forum, where I'd need to bring it up, try and get some buy-in around my change concept, then put together an RFC when enough(?) people have shown interest, and then finally that RFC would point to an implementation.

Builders

The builder pattern is super common in Rust. Almost obnoxiously so. To the point where I wonder if Rust should have some sort of first-class support or new language feature that largely obsoletes the need for the pattern.

Solutions

In general the pattern is often replaced by things like variadic functions or ad-hoc option arguments in other languages.

This isn't a terrible pattern, but even going as far as introducing https://crates.io/crates/derive_builder into the language or into the Book would be nice.

Multi-type errors

The book doesn't really cover a pretty basic case in earnest: the situation where you have multiple different error returns and you want to use ?.

Solutions

Establishing an error-definition pattern a-la https://crates.io/crates/failure in the Book would be super helpful, if not integrating failure itself.

Update: I'm supposed to use failure, not error-chain, per @ag_dubs. SIGH

Update: The plot thickens! https://twitter.com/hoodie_de/status/1135192684916879360 claims I should not be returning failure::Error from public APIs but this is not clear at all and I'm not sure what the expected alternative is.

Returning iterators

Iterators are super duper useful! Infortunately, it's super hard to figure out how to -return- them from functions. The compiler errors when you first try to do this are incredibly opaque and confusing.

Solution

It turns out these days, you have -> impl Iterator which makes this work, but this was not very obvious and the compiler errors were SUPER CONFUSING about how to fix this. It might go a long way to have the compiler be aware of at least the Iterator case. Those compiler messages, wwhoo.

WTF is AsRef?

I kept seeing this AsRef stuff while working with Path-related functionality and didn't really understand why folks were doing that instead of path: &Path in function signatures.

Solutions

This isn't really covered in the Book, and maybe it should. A friend proceeded to explain the use and intent: It's used as a perf-cheap version of From to convert from one immutable ref to another immutable ref. In this particular case, it was being used so you could pass Paths, PathBufs, or strings into the function and have it work with any of those types.

It would be nice to make a note in the Book about this being a convention, or becoming one?

Weird File API

The File-related APIs are really strange and factored in such a way that I had a really hard time finding what I needed, compared to the relatively flat fs module in Node.js.

I think the biggest pain is that some things seem to live in fs::, some in io:: and others in fs::File, and it wasn't clear when one vs the other was used. The toplevel fs:: utilities are actually pretty handy, though!

Solutions

idk. Changing this fundamentally would have a huge impact, unfortunately, and Rust is very stability-oriented these days. I kinda wish there were a simpler, higher-level, zero-cost crate for doing these things, optionally with Futures, and for the fs and io stuff to largely be considered "internal use only" building blocks, if even that. I just plain don't like it and find it hard to work with. Maybe Tokio is the answer, once it's updated with futures@0.3?

Literally no chown?

So the fs API has literally no chown functionality. I know it's pretty niche, but I needed it!

Solutions

This is exposed through direct bindings by nix. There's also a higher-level chownr crate by yours truly that does the recursive bit.

Everyone uses extern crate

...but you're supposed to just use use. This is mostly an annoyance because everyone's docs are outdated and still refer people to importing using this. As an additional note, a lot of places combine this statement with #![macro_use] when importing macros.

Solutions

Highlighting that extern crate isn't used anymore, in the Book, might be super helpful here. The rest is just waiting out the ecosystem. I'm not really sure what the actual expected alternative to #![macro_use] is supposed to be, but I had some luck directly use-ing the macro into my scope like a function? So a macro named foo in crate bar would be imported with use bar::foo; and then the macro seemed to work...

No cargo version built-in

Coming from Node, this feels like a sharp corner: I have to manually edit Cargo.toml, change the version, run cargo build to update the Cargo.lock so I don't end up with a random diff, then I have to manually git ci -a -m 'vX.Y.Z', git tag -a vX.Y.Z, fill in a message, git push --follow-tags, and then cargo publish. Just for a basic release. I'm used to at least having a baseline of npm version patch && git push --follow-tags && npm pub.

Solution

Having a baseline cargo version built in would go a long way towards making publishing as a newbie a smoother process.

No nyc equivalent for coverage

There's no single coverage tool you can just install that integrates with cargo test and gives useful coverage feedback in the command line, works with coveralls/codecov, etc. The best you get is something as described in this 3-year-old forum thread which... honestly I'm not going to bother because this is an immense amount of fucking around with stuff just to get my percentages?

I'm so used to having nyc available :()

Solution

Rust -really- needs an nyc port.