Naming

Casing conforms to RFC 430 (C-CASE)

Basic Rust naming conventions are described in RFC 430.

In general, Rust tends to use UpperCamelCase for "type-level" constructs (types and traits) and snake_case for "value-level" constructs. More precisely:

Item Convention
Crates unclear
Modules snake_case
Types UpperCamelCase
Traits UpperCamelCase
Enum variants UpperCamelCase
Functions snake_case
Methods snake_case
General constructors new or with_more_details
Conversion constructors from_some_other_type
Macros snake_case!
Local variables snake_case
Statics SCREAMING_SNAKE_CASE
Constants SCREAMING_SNAKE_CASE
Type parameters concise UpperCamelCase, usually single uppercase letter: T
Lifetimes short lowercase, usually a single letter: 'a, 'de, 'src
Features unclear but see C-FEATURE

In UpperCamelCase, acronyms and contractions of compound words count as one word: use Uuid rather than UUID, Usize rather than USize or Stdin rather than StdIn. In snake_case, acronyms and contractions are lower-cased: is_xid_start.

In snake_case or SCREAMING_SNAKE_CASE, a "word" should never consist of a single letter unless it is the last "word". So, we have btree_map rather than b_tree_map, but PI_2 rather than PI2.

Crate names should not use -rs or -rust as a suffix or prefix. Every crate is Rust! It serves no purpose to remind users of this constantly.

Examples from the standard library

The whole standard library. This guideline should be easy!

Ad-hoc conversions follow as_, to_, into_ conventions (C-CONV)

Conversions should be provided as methods, with names prefixed as follows:

Prefix Cost Ownership
as_ Free borrowed -> borrowed
to_ Expensive borrowed -> borrowed
borrowed -> owned (non-Copy types)
owned -> owned (Copy types)
into_ Variable owned -> owned (non-Copy types)

For example:

  • str::as_bytes() gives a view of a str as a slice of UTF-8 bytes, which is free. The input is a borrowed &str and the output is a borrowed &[u8].
  • Path::to_str performs an expensive UTF-8 check on the bytes of an operating system path. The input and output are both borrowed. It would not be correct to call this as_str because this method has nontrivial cost at runtime.
  • str::to_lowercase() produces the Unicode-correct lowercase equivalent of a str, which involves iterating through characters of the string and may require memory allocation. The input is a borrowed &str and the output is an owned String.
  • f64::to_radians() converts a floating point quantity from degrees to radians. The input is f64. Passing a reference &f64 is not warranted because f64 is cheap to copy. Calling the function into_radians would be misleading because the input is not consumed.
  • String::into_bytes() extracts the underlying Vec<u8> of a String, which is free. It takes ownership of a String and returns an owned Vec<u8>.
  • BufReader::into_inner() takes ownership of a buffered reader and extracts out the underlying reader, which is free. Data in the buffer is discarded.
  • BufWriter::into_inner() takes ownership of a buffered writer and extracts out the underlying writer, which requires a potentially expensive flush of any buffered data.

Conversions prefixed as_ and into_ typically decrease abstraction, either exposing a view into the underlying representation (as) or deconstructing data into its underlying representation (into). Conversions prefixed to_, on the other hand, typically stay at the same level of abstraction but do some work to change one representation into another.

When a type wraps a single value to associate it with higher-level semantics, access to the wrapped value should be provided by an into_inner() method. This applies to wrappers that provide buffering like BufReader, encoding or decoding like GzDecoder, atomic access like AtomicBool, or any similar semantics.

If the mut qualifier in the name of a conversion method constitutes part of the return type, it should appear as it would appear in the type. For example Vec::as_mut_slice returns a mut slice; it does what it says. This name is preferred over as_slice_mut.


# #![allow(unused_variables)]
#fn main() {
// Return type is a mut slice.
fn as_mut_slice(&mut self) -> &mut [T];
#}
More examples from the standard library

Getter names follow Rust convention (C-GETTER)

With a few exceptions, the get_ prefix is not used for getters in Rust code.


# #![allow(unused_variables)]
#fn main() {
pub struct S {
    first: First,
    second: Second,
}

impl S {
    // Not get_first.
    pub fn first(&self) -> &First {
        &self.first
    }

    // Not get_first_mut, get_mut_first, or mut_first.
    pub fn first_mut(&mut self) -> &mut First {
        &mut self.first
    }
}
#}

The get naming is used only when there is a single and obvious thing that could reasonably be gotten by a getter. For example Cell::get accesses the content of a Cell.

For getters that do runtime validation such as bounds checking, consider adding unsafe _unchecked variants. Typically those will have the following signatures.


# #![allow(unused_variables)]
#fn main() {
fn get(&self, index: K) -> Option<&V>;
fn get_mut(&mut self, index: K) -> Option<&mut V>;
unsafe fn get_unchecked(&self, index: K) -> &V;
unsafe fn get_unchecked_mut(&mut self, index: K) -> &mut V;
#}

The difference between getters and conversions (C-CONV) can be subtle and is not always clear-cut. For example TempDir::path can be understood as a getter for the filesystem path of the temporary directory, while TempDir::into_path is a conversion that transfers responsibility for deleting the temporary directory to the caller. Since path is a getter, it would not be correct to call it get_path or as_path.

Examples from the standard library

Methods on collections that produce iterators follow iter, iter_mut, into_iter (C-ITER)

Per RFC 199.

For a container with elements of type U, iterator methods should be named:


# #![allow(unused_variables)]
#fn main() {
fn iter(&self) -> Iter             // Iter implements Iterator<Item = &U>
fn iter_mut(&mut self) -> IterMut  // IterMut implements Iterator<Item = &mut U>
fn into_iter(self) -> IntoIter     // IntoIter implements Iterator<Item = U>
#}

This guideline applies to data structures that are conceptually homogeneous collections. As a counterexample, the str type is slice of bytes that are guaranteed to be valid UTF-8. This is conceptually more nuanced than a homogeneous collection so rather than providing the iter/iter_mut/into_iter group of iterator methods, it provides str::bytes to iterate as bytes and str::chars to iterate as chars.

This guideline applies to methods only, not functions. For example percent_encode from the url crate returns an iterator over percent-encoded string fragments. There would be no clarity to be had by using an iter/iter_mut/into_iter convention.

Examples from the standard library

Iterator type names match the methods that produce them (C-ITER-TY)

A method called into_iter() should return a type called IntoIter and similarly for all other methods that return iterators.

This guideline applies chiefly to methods, but often makes sense for functions as well. For example the percent_encode function from the url crate returns an iterator type called PercentEncode.

These type names make the most sense when prefixed with their owning module, for example vec::IntoIter.

Examples from the standard library

Feature names are free of placeholder words (C-FEATURE)

Do not include words in the name of a Cargo feature that convey zero meaning, as in use-abc or with-abc. Name the feature abc directly.

This arises most commonly for crates that have an optional dependency on the Rust standard library. The canonical way to do this correctly is:

# In Cargo.toml

[features]
default = ["std"]
std = []

# #![allow(unused_variables)]
#fn main() {
// In lib.rs

#![cfg_attr(not(feature = "std"), no_std)]
#}

Do not call the feature use-std or with-std or any creative name that is not std. This naming convention aligns with the naming of implicit features inferred by Cargo for optional dependencies. Consider crate x with optional dependencies on Serde and on the Rust standard library:

[package]
name = "x"
version = "0.1.0"

[features]
std = ["serde/std"]

[dependencies]
serde = { version = "1.0", optional = true }

When we depend on x, we can enable the optional Serde dependency with features = ["serde"]. Similarly we can enable the optional standard library dependency with features = ["std"]. The implicit feature inferred by Cargo for the optional dependency is called serde, not use-serde or with-serde, so we like for explicit features to behave the same way.

As a related note, Cargo requires that features are additive so a feature named negatively like no-abc is practically never correct.

Names use a consistent word order (C-WORD-ORDER)

Here are some error types from the standard library:

All of these use verb-object-error word order. If we were adding an error to represent an address failing to parse, for consistency we would want to name it in verb-object-error order like ParseAddrError rather than AddrParseError.

The particular choice of word order is not important, but pay attention to consistency within the crate and consistency with similar functionality in the standard library.