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 astr
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 thisas_str
because 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&str
and the output is an ownedString
.f64::to_radians()
converts a floating point quantity from degrees to radians. The input isf64
. Passing a reference&f64
is not warranted becausef64
is cheap to copy. Calling the functioninto_radians
would be misleading because the input is not consumed.String::into_bytes()
extracts the underlyingVec<u8>
of aString
, which is free. It takes ownership of aString
and 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_mut
std::ptr::Unique::get_mut
std::sync::PoisonError::get_mut
std::sync::atomic::AtomicBool::get_mut
std::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::iter
returnsIter
Vec::iter_mut
returnsIterMut
Vec::into_iter
returnsIntoIter
BTreeMap::keys
returnsKeys
BTreeMap::values
returnsValues
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:
JoinPathsError
ParseBoolError
ParseCharError
ParseFloatError
ParseIntError
RecvTimeoutError
StripPrefixError
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.