For example, this is why the Box::into_raw
function is defined the way it is.
#![allow(unused)] fn main() { impl<T> Box<T> where T: ?Sized { fn into_raw(b: Box<T>) -> *mut T { /* ... */ } } let boxed_str: Box<str> = /* ... */; let ptr = Box::into_raw(boxed_str); }
If this were defined as an inherent method instead, it would be confusing at the call site whether the method being called is a method on Box<T>
or a method on T
.
Conversions live on the most specific type involved (C-CONV-SPECIFIC)#![allow(unused)] fn main() { impl<T> Box<T> where T: ?Sized { // Do not do this. fn into_raw(self) -> *mut T { /* ... */ } } let boxed_str: Box<str> = /* ... */; // This is a method on str accessed through the smart pointer Deref impl. boxed_str.chars() // This is a method on Box<str>...? boxed_str.into_raw() }
When in doubt, prefer to_
/as_
/into_
to from_
, because they are more ergonomic to use (and can be chained with other methods).
For many conversions between two types, one of the types is clearly more "specific": it provides some additional invariant or interpretation that is not present in the other type. For example, str
is more specific than &[u8]
, since it is a UTF-8 encoded sequence of bytes.
Conversions should live with the more specific of the involved types. Thus, str
provides both the as_bytes
method and the from_utf8
constructor for converting to and from &[u8]
values. Besides being intuitive, this convention avoids polluting concrete types like &[u8]
with endless conversion methods.
Prefer
#![allow(unused)] fn main() { impl Foo { pub fn frob(&self, w: widget) { /* ... */ } } }
over
#![allow(unused)] fn main() { pub fn frob(foo: &Foo, w: widget) { /* ... */ } }
for any operation that is clearly associated with a particular type.
Methods have numerous advantages over functions:
T
" (especially when using rustdoc).self
notation, which is more concise and often more clearly conveys ownership distinctions.Prefer
#![allow(unused)] fn main() { fn foo() -> (Bar, Bar) }
over
#![allow(unused)] fn main() { fn foo(output: &mut Bar) -> Bar }
for returning multiple Bar
values.
Compound return types like tuples and structs are efficiently compiled and do not require heap allocation. If a function needs to return multiple values, it should do so via one of these types.
The primary exception: sometimes a function is meant to modify data that the caller already owns, for example to re-use a buffer:
Operator overloads are unsurprising (C-OVERLOAD)#![allow(unused)] fn main() { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> }
Operators with built in syntax (*
, |
, and so on) can be provided for a type by implementing the traits in std::ops
. These operators come with strong expectations: implement Mul
only for an operation that bears some resemblance to multiplication (and shares the expected properties, e.g. associativity), and so on for the other traits.
Deref
and DerefMut
(C-DEREF)
The Deref
traits are used implicitly by the compiler in many circumstances, and interact with method resolution. The relevant rules are designed specifically to accommodate smart pointers, and so the traits should be used only for that purpose.
In Rust, "constructors" are just a convention. There are a variety of conventions around constructor naming, and the distinctions are often subtle.
A constructor in its most basic form is a new
method with no arguments.
#![allow(unused)] fn main() { impl<T> Example<T> { pub fn new() -> Example<T> { /* ... */ } } }
Constructors are static (no self
) inherent methods for the type that they construct. Combined with the practice of fully importing type names, this convention leads to informative but concise construction:
#![allow(unused)] fn main() { use example::Example; // Construct a new Example. let ex = Example::new(); }
The name new
should generally be used for the primary method of instantiating a type. Sometimes it takes no arguments, as in the examples above. Sometimes it does take arguments, like Box::new
which is passed the value to place in the Box
.
Some types' constructors, most notably I/O resource types, use distinct naming conventions for their constructors, as in File::open
, Mmap::open
, TcpStream::connect
, and UdpSocket::bind
. In these cases names are chosen as appropriate for the domain.
Often there are multiple ways to construct a type. It's common in these cases for secondary constructors to be suffixed _with_foo
, as in Mmap::open_with_offset
. If your type has a multiplicity of construction options though, consider the builder pattern (C-BUILDER) instead.
Some constructors are "conversion constructors", methods that create a new type from an existing value of a different type. These typically have names beginning with from_
as in std::io::Error::from_raw_os_error
. Note also though the From
trait (C-CONV-TRAITS), which is quite similar. There are three distinctions between a from_
-prefixed conversion constructor and a From<T>
impl.
from_
constructor can be unsafe; a From
impl cannot. One example of this is Box::from_raw
.from_
constructor can accept additional arguments to disambiguate the meaning of the source data, as in u64::from_str_radix
.From
impl is only appropriate when the source data type is sufficient to determine the encoding of the output data type. When the input is just a bag of bits like in u64::from_be
or String::from_utf8
, the conversion constructor name is able to identify their meaning.Note that it is common and expected for types to implement both Default
and a new
constructor. For types that have both, they should have the same behavior. Either one may be implemented in terms of the other.
std::io::Error::new
is the commonly used constructor for an IO error.std::io::Error::from_raw_os_error
is a conversion constructor based on an error code received from the operating system.Box::new
creates a new container type, taking a single argument.File::open
opens a file resource.Mmap::open_with_offset
opens a memory-mapped file, with additional options.RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4