Llogiq on stuff

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.