Rust in 2021
Who is this guy anyway?
Hello, it's me! I'm a somewhat new contributor to Rust and I'm about three blog posts behind. Here they are all at once:
- I work on docs.rs!
- I wrote a C compiler in Rust
- I spend way too much time working on intra-doc links
- I help make it easier to contribute to the Rust compiler
Wait, we're writing a blog post here.
In particular I want to talk about that last point. I spend way too much time on Discord and I commonly run into questions like this:
How do I get the first element of a vector if I don't care about the rest?
vec[0]
gives me a reference.
How does docs.rs calculate the percentage of items documented in a crate?
Why doesn't
async || { ... }
work? The compiler told me to addasync
if I want to useawait
!
Why am I getting a borrow check error when I try to add something to a map if it's not yet there?
How can I use generic types in an
extern "C"
function? Is this even possible? (This one I asked!)
How do I link to the latest version of a subpage of my docs on docs.rs?
Is there a guide on how to add a new target or promote a target to tier 2?
How can I use C or C++ functions from Rust? (not just the C standard library, but other libraries)
Can I have an API that takes either a value or a reference?
Can I have a logging function that will print then return a value?
Where did
arg_enum!
end up in clap 3? It's not atclap::arg_enum
like it was before.
Is there a way to do this with iterator methods instead?
All of these have answers that are simple to understand after the fact, but are very hard to figure out if you don't already know them.
Discoverability
As a frequent contributor to the compiler, the theme I want to focus on in 2021 is discoverability. Rust and the Rust ecosystem have a lot of features, but it can be hard to find them all, or even to know that they exist - you can't search if you don't know what to search for!
In the compiler
Rust has a well-earned reputation for good error messages. I want to continue to expand those to catch more common errors and guide you in the right direction. There is a lot of great work going on this area: A giant thank you to @estebank, @da-x, and everyone else working on improving diagnostics!
In libraries
This one is harder - libraries can't give error messages, the best they can do is write documentation. I want to see more examples of ways to solve errors using the library. For example, imagine if this error:
error: cannot borrow `*map` as mutable more than once at a time
-/main.rs:14:13
|
6 | | -- lifetime `'m` defined here
...
11 | match map.get_mut
| |_____- returning this value requires that `*map` is borrowed for `'m`
also said this:
= note: this pattern is valid, but not currently recognized by the borrow-checker: https://doc.rust-lang.org/nomicon/lifetime-mismatch.html#improperly-reduced-borrows
= help: try using the `Entry` API:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another example:
error: failed to resolve: could not find `arg_enum` in `clap`
-/lib.rs:2:10
|
2 | arg_enum!
Could instead be:
error: `arg_enum` is now a derive macro
-/lib.rs:2:10
|
2 | arg_enum!
Not only would it help fix the code, but it would help you learn more about the library and the Rust language! I'm sure there's many things making this difficult, but I think it's a great goal to reach for.
In devtools
This one I think has both the most room for improvement and needs the least effort to fix.
Currently, a lot of functionality is 'hidden' behind cargo
subcommands.
For examples, cargo test -- --ignored
and cargo test -- --test-threads 1
are very difficult to find unless you see existing examples online.
Even experienced rustaceans don't know about many rustdoc options because they need
cargo rustdoc -- -h
, not cargo doc -h
or cargo doc -- -h
.
I think we should both document these options better and make them easier to find by experimenting.
Another improvement I see in devtools is distinguishing between stable and unstable features. Right now the way to do this is different for every tool:
cargo
hascargo-features = ["..."]
rustfmt
hasunstable_features = true
, including on the stable channelrustdoc
enables most features by default, even without#![feature(...)]
This causes confusion: In the words of @abonader,
I assume it works on stable if there's no feature flags.
I think there should be a consistent story here that makes it easier to see what's nightly and what isn't (even I got confused by this!).
In the community
It can be difficult to know where discussions take place. There are many different places:
- Zulip
- Discord
- rustc-dev-guide
- Forge
- Blog posts (usually discussed on reddit)
- internals.rust-lang.org
- users.rust-lang.org
- MCPs in rust-lang/compiler
- MCPs in rust-lang/lang
- RFCs in rust-lang/rfcs
- FCPs in rust-lang/rust
- Various READMEs in the compiler itself (although most of those point to external links now)
This makes it hard for new contributors to find information and hard for frequent contributors to stay up to date on the current status of issues.
I think we should consolidate documentation into fewer places and discussion into fewer channels. This would make it easier to understand what's going on without having to spend lots of time chasing down leads (in both senses 😉).
What do you think could be more discoverable?
Is there a feature of Rust you really enjoy that people don't know about? Are you looking for a feature that seems like it should exist but you haven't been able to find? Please let me know! Good ways to bring this up are writing other blog posts or opening an issue.
Summary
I think Rust is a great language with a bright future. I want to make it easier not just to get started, but to explore and learn more about Rust and the Rust community.
FAQ
You can't have questions without answers!
How do I get the first element of a vector if I don't care about the rest?
vec[0]
gives me a reference.
vec.into_iter().next()
or vec.truncate(1); vec.pop()
depending on when you want to drop the items (discussion).
How does docs.rs calculate the percentage of items documented in a crate?
Why doesn't
async || { ... }
work? The compiler told me to addasync
if I want to useawait
!
Use async { ... }
instead. I opened an issue to add a suggestion for this.
Why am I getting a borrow check error when I try to add something to a map if it's not yet there?
This is a limitation of the borrow checker; use entry()
instead.
How can I use generic types in an
extern "C"
function? Is this even possible? (This one I asked!)
It works like a normal function pointer! You can simply use my_c_func(my_callback::<T> as fn()
,
and declare my_callback
like a normal generic function that happens to have extern "C"
at the front.
How do I link to the latest version of a subpage of my docs on docs.rs?
/my-crate/latest/my_crate/module/kind.name.html
; docs.
Is there a guide on how to add a new target or promote a target to tier 2?
There is an RFC in progress and docs for adding a new target. However I'm not aware of any documentation for promoting a target from tier 3 to tier 2, or from 'tier 2 cross-compiled' to 'tier 2 hosted' (see the RFC).
How can I use C or C++ functions from Rust? (not just the C standard library, but other libraries)
You probably want either bindgen or cxx. For the reverse, calling Rust from C or C++, take a look at cbindgen.
Can I have an API that takes either a value or a reference?
Yes, Borrow<T>
.
NOTE: this answer originally said AsRef<T>
,
which doesn't always work - there's no blanket impl AsRef<T> for &T
like there is for Borrow
. This difference is itself a discoverability problem! Thanks to @raphlinus for pointing out the difference.
Can I have a logging function that will print then return a value?
Yes, and it's even in the standard library: dbg!
.
Unfortunately it doesn't work with log
or tracing
,
but it's not too hard to write your own wrapper around those.
Where did
arg_enum!
end up in clap 3? It's not atclap::arg_enum
like it was before.
It's now a derive macro.
Is there a way to do this with iterator methods instead?
Yes.
Updates!
This was surprisingly popular on reddit! I'm answering a few of the most relevant questions here so everyone can see them.
Is there a way a library can give a custom message on trait errors?
Yes, rustc_on_unimplemented
. Unfortunately it's not intended to be stabilized,
but I'd be happy to see an RFC for it!
Is there something like Hoogle for Rust, where you give it a type signature and it suggests possible implementations?
Yes, rustdoc has 'type-based search'. Press ?
in any rustdoc page for more information about it.
Couldn't the maintainers of clap make
arg_enum!
deprecated and point to the derive macro?
No, because both the old and new macro have the same name in the same namespace. So adding both would be a compile error. Fortunately this means the compiler can help out, I opened an issue for it to do so.