as, to and into methods in rust

15 July 2020. Estimated reading time: 3 min.

Rust standard library types frequently have methods with names that look like as_something, to_something, or into_something. Clearly the difference between "as", "to" and "into" is intentional, and I realized I didn't know why.

So let's pick String, and take a closer look at the as/to/into methods that are available.

pub fn to_lowercase(&self) -> String;
pub fn into_bytes(self) -> Vec<u8>;
pub fn as_bytes(&self) -> &[u8];

In the to_lowercase method, it's clear from the function name that it may need to produce new characters, and we also see that it only borrows the string, but returns a new owned string. Clearly then it's allocating a new string, and filling it with characters either copied or converted from the source string.

If we check the source code, we see that's indeed the case.

pub fn to_lowercase(&self) -> String {
    let mut s = String::with_capacity(self.len());
    for (i, c) in self[..].char_indices() {
        // ... push chars into `s`
    return s;

The into_bytes method is similar in that it too returns an owned value. However, it's called "into" and, unlike "to", the source string is moved. We can make an educated guess that underlying representation is reused by the Vec, otherwise it would make more sense to make this a "to" method, and preserve the original string.

In fact, String is implemented over a Vec, so this method merely consumes the String wrapper and returns the underlying Vec.

pub fn into_bytes(self) -> Vec<u8> {

Finally, as_bytes takes and returns a reference. It's a conversion, but a lightweight one, in which the underlying data in memory is shared by both source and destination values.

Checking the source code we see that it too uses the underlying Vec, but returns a slice from it, instead of the Vec itself.

pub fn as_bytes(&self) -> &[u8] {

So, what can we extrapolate from this?

"As" methods perform lightweight conversions, and both take and return a reference to the same underlying data in memory. No cloning involved.

"To" methods perform conversions that require copying the underlying data, sometimes just because the return type is useful because it's owned, like when calling to_string() on a &str.

And "Into" methods are suitable for conversions that require taking ownership of the source value to be efficient and avoid duplication of data. They are also aptly named: they are "to" methods that take in the original value.

Last updated: 15 July 2020.