Naming
Casing conforms to RFC 430 (C-CASE)
Basic Rust naming conventions are described inRFC 430.
In general, Rust tends to useUpperCamelCase
for "type-level" constructs (types andtraits) andsnake_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 orwith_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 | conciseUpperCamelCase , usually single uppercase letter:T |
Lifetimes | shortlowercase , usually a single letter:'a ,'de ,'src |
Features | unclear but seeC-FEATURE |
InUpperCamelCase
, acronyms and contractions of compound words count as one word: useUuid
rather thanUUID
,Usize
rather thanUSize
orStdin
rather thanStdIn
. Insnake_case
, acronyms and contractions are lower-cased:is_xid_start
.
Insnake_case
orSCREAMING_SNAKE_CASE
, a "word" should never consist of asingle letter unless it is the last "word". So, we havebtree_map
rather thanb_tree_map
, butPI_2
rather thanPI2
.
Crate names should not use-rs
or-rust
as a suffix or prefix. Every crateis 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 followas_
,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, whichis 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 anoperating system path. The input and output are both borrowed. It would not becorrect to call thisas_str
because this method has nontrivial cost atruntime.str::to_lowercase()
produces the Unicode-correct lowercase equivalent of astr
, which involves iterating through characters of the string and mayrequire memory allocation. The input is a borrowed&str
and the output is anownedString
.f64::to_radians()
converts a floating point quantity from degrees toradians. The input isf64
. Passing a reference&f64
is not warrantedbecausef64
is cheap to copy. Calling the functioninto_radians
would bemisleading 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 extractsout the underlying reader, which is free. Data in the buffer is discarded.BufWriter::into_inner()
takes ownership of a buffered writer and extractsout the underlying writer, which requires a potentially expensive flush of anybuffered data.
Conversions prefixedas_
andinto_
typicallydecrease abstraction, eitherexposing a view into the underlying representation (as
) or deconstructing datainto its underlying representation (into
). Conversions prefixedto_
, on theother hand, typically stay at the same level of abstraction but do some work tochange 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 aninto_inner()
method. Thisapplies to wrappers that provide buffering likeBufReader
, encoding ordecoding likeGzDecoder
, atomic access likeAtomicBool
, or any similarsemantics.
If themut
qualifier in the name of a conversion method constitutes part ofthe return type, it should appear as it would appear in the type. For exampleVec::as_mut_slice
returns a mut slice; it does what it says. This name ispreferred overas_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, theget_
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 }}}
Theget
naming is used only when there is a single and obvious thing thatcould reasonably be gotten by a getter. For exampleCell::get
accesses thecontent of aCell
.
For getters that do runtime validation such as bounds checking, consider addingunsafe_unchecked
variants. Typically those will have the followingsignatures.
#![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 subtleand is not always clear-cut. For exampleTempDir::path
can be understood asa getter for the filesystem path of the temporary directory, whileTempDir::into_path
is a conversion that transfers responsibility fordeleting the temporary directory to the caller. Sincepath
is a getter, itwould not be correct to call itget_path
oras_path
.
Examples from the standard library
std::io::Cursor::get_mut
std::pin::Pin::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 followiter
,iter_mut
,into_iter
(C-ITER)
PerRFC 199.
For a container with elements of typeU
, 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 homogeneouscollections. As a counterexample, thestr
type is slice of bytes that areguaranteed to be valid UTF-8. This is conceptually more nuanced than ahomogeneous collection so rather than providing theiter
/iter_mut
/into_iter
group of iterator methods, it providesstr::bytes
to iterate as bytes andstr::chars
to iterate as chars.
This guideline applies to methods only, not functions. For examplepercent_encode
from theurl
crate returns an iterator over percent-encodedstring fragments. There would be no clarity to be had by using aniter
/iter_mut
/into_iter
convention.
Examples from the standard library
Iterator type names match the methods that produce them (C-ITER-TY)
A method calledinto_iter()
should return a type calledIntoIter
andsimilarly for all other methods that return iterators.
This guideline applies chiefly to methods, but often makes sense for functionsas well. For example thepercent_encode
function from theurl
cratereturns an iterator type calledPercentEncode
.
These type names make the most sense when prefixed with their owning module, forexamplevec::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 aCargo feature that convey zero meaning,as inuse-abc
orwith-abc
. Name the featureabc
directly.
This arises most commonly for crates that have an optional dependency on theRust 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 featureuse-std
orwith-std
or any creative name that is notstd
. This naming convention aligns with the naming of implicit featuresinferred by Cargo for optional dependencies. Consider cratex
with optionaldependencies 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 onx
, we can enable the optional Serde dependency withfeatures = ["serde"]
. Similarly we can enable the optional standard librarydependency withfeatures = ["std"]
. The implicit feature inferred by Cargo forthe optional dependency is calledserde
, notuse-serde
orwith-serde
, sowe like for explicit features to behave the same way.
As a related note, Cargo requires that features are additive so a feature namednegatively likeno-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 torepresent an address failing to parse, for consistency we would want to name itin verb-object-error order likeParseAddrError
rather thanAddrParseError
.
The particular choice of word order is not important, but pay attention toconsistency within the crate and consistency with similar functionality in thestandard library.