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 astras a slice of UTF-8 bytes, which is free. The input is a borrowed&strand the output is a borrowed&[u8].Path::to_strperforms 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 thisas_strbecause this method has nontrivial cost at runtime.str::to_lowercase()produces the Unicode-correct lowercase equivalent of astr, which involves iterating through characters of the string and may require memory allocation. The input is a borrowed&strand the output is an ownedString.f64::to_radians()converts a floating point quantity from degrees to radians. The input isf64. Passing a reference&f64is not warranted becausef64is cheap to copy. Calling the functioninto_radianswould be misleading because the input is not consumed.String::into_bytes()extracts the underlyingVec<u8>of aString, which is free. It takes ownership of aStringand returns an ownedVec<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 from one representation to 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)] 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)] 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)] 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
std::io::Cursor::get_mutstd::pin::Pin::get_mutstd::sync::PoisonError::get_mutstd::sync::atomic::AtomicBool::get_mutstd::collections::hash_map::OccupiedEntry::get_mut<[T]>::get_unchecked
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)] 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
Vec::iterreturnsIterVec::iter_mutreturnsIterMutVec::into_iterreturnsIntoIterBTreeMap::keysreturnsKeysBTreeMap::valuesreturnsValues
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)] fn main() { // In lib.rs #![no_std] #[cfg(feature = "std")] extern crate 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:
JoinPathsErrorParseBoolErrorParseCharErrorParseFloatErrorParseIntErrorRecvTimeoutErrorStripPrefixError
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.