Rustic Bits
Some small things that make for rustic code:
Result<(), ()>
== bool
, but the compiler will warn if unused.
Similarly Result<T, ()>
== Option<T>
, with the same compiler
warning. Use this type for results where the error case has no
additional information. Also Result<(), E>
for operations that can
err but return no value otherwise.
Consider using an enum MyEnum { SomeState, OtherState }
instead of
bool for distinguishing two states when used in function arguments to
clarify intent.
While some other languages regard shadowing as something evil, it sometimes
can be used to avoid mutation, e.g. let x = x + 1;
creates a new
implicit scope within which x
means the increment of the x
outside
of that scope. However beware the scoping rules – the following code
snippets are different:
let mut x = 0; let x = 0;
if something { if something {
x = x + 1; let x = x + 1;
foo(x); foo(x);
} }
bar(x); bar(x);
The program on the left calls bar(1)
whereas the one on the right
calls bar(0)
.
Often you don’t need a String
. See if a Cow<'a, str>
or even a
&str
suffices. Same goes for other owned types.
Resist the urge to .clone()
to appease the borrow checker.
(Courtesy of /u/mbrubeck)
You can use rust-phf to build your hash table at compile time, with no heap allocations. Requires either a compiler plugin (nightly only) or code generation using a build.rs script.
Generally while it’s not exactly easy in Rust, putting static immutable data in our programs is an often underused technique.
Name your methods consistently to their self
argument:
method | self argument |
---|---|
from_ .. |
(none) |
new |
(none) |
as_ .. |
&self / &mut self |
into_ .. |
self |
to_ .. |
&self |
is_ .. |
&self (or none) |
has_ .. |
&self (or none) |
Also implement existing traits where applicable.
Being careful to keep function arguments general will make your code more
flexible. One example of this is using slices (&[T]
) instead of
Vec<T>
s. Another is making functions generic over input traits, e.g.
fn<P: AsRef<Path>> with_path(p: P) { .. }
.
If you’re inserting or updating into a BTreeMap
or HashMap
, use the Entry
API. This keeps you from doing double lookups and usually make your code more
readable, too.
Make use of the ecosystem – use crates.io to see if someone else has already written it. By the way, you can look for a crate’s reverse dependencies to see how widely it is used.
Disagree about something? Am I missing things? What are the small techniques that make your code rustic? Discuss on r/rust or rust-users.