What is the matter with `AsRef`?
Tags: rust, beginners, programming, bites
Published: 2022-08-09 15:33:11

---
title: What is the matter with `AsRef`?
published: true
description: the `AsRef` trait is everywhere in the std lib and very handy. However, the benefit using it is maybe not too obvious. But read on, and you will see some useful examples and where to apply it. 
tags: rust,beginners,programming,bites
canonical_url: https://d34dl0ck.me/rust-bites-as-ref/index.html
series: practical rust bites
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
---

the `AsRef` trait is everywhere in the std lib and very handy. However, the benefit using it is maybe not too obvious. But read on, and you will see some useful examples and where to apply it.

This is the 2nd post on the series ["practical rust bites"](https://d34dl0ck.me/tags/practical-rust-bites/index.html) that shows very tiny pieces of rust taken out of practical real projects. It aims to illustrate only one rust idiom or concept at a time with very practical examples.

## Background

`AsRef` can be [found in the](https://doc.rust-lang.org/std/convert/trait.AsRef.html) `std::convert` module and is used 
for cheap reference-to-reference conversion.

It's a very essential trait widely used in the std library and helps with seamless reference conversions from one type to another. 
Often it is at play, and you do not even realize it is there.

## Example: `String` or `&str`

Did you ever have had a function where you wanted to accept a string as a parameter?
You might then also have asked yourself should you accept a string reference (as in `&str`) or an owned string (as in `String`)?

So you would have something like this in mind:
```rust
fn take_a_str(some: &str) {}
```
and the other one:
```rust
fn take_a_string(some: String) {}
```

> So why not have both?!

At this very point `AsRef<str>` comes in very handy, 
because both types [`str`](https://doc.rust-lang.org/std/primitive.str.html#impl-AsRef%3Cstr%3E) and [`String`](https://doc.rust-lang.org/std/primitive.str.html#impl-AsRef%3Cstr%3E-1) implement this trait.

```rust
fn take_a_str(some: impl AsRef<str>) {
    let some = some.as_ref();
    println!("{some}");
}

fn main() {
    take_a_str("str");
    take_a_str("String".to_string());
    
    // also `&String` is supported:
    let string_ref = "StringRef".to_string();
    take_a_str(&string_ref);
}
```

[check out the code on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ccb7bce2bc6db0dd2e5e5a58fa7e9c61)

As you can see this version of `take_a_str` accepts a type that just implements `AsRef<str>`.

By doing so `some: impl AsRef<str>` does not make any concrete assumptions on the type that is provided. 
Instead, it just insists that any given type only implements this trait.

## Example: wrapper type

In this example we want to focus on how can you implement `AsRef` yourself for any arbitrary struct.

Let's look at this tiny program:
```rust
pub struct Envelope {
    letter: String
}

fn main() {
    let a_letter = Envelope { 
        letter: "a poem".to_string() 
    };

    println!("this is a letter: {}", &a_letter);
}
```

But of course you'd say now, well, it does not implement `Display` and so does the rust compiler agree with you:
```plain
error[E0277]: `Envelope` doesn't implement `std::fmt::Display`
  --> src/main.rs:10:38
   |
10 |     println!("this is a letter: {}", &a_letter);
   |                                      ^^^^^^^^^ `Envelope` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `Envelope`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
   = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.
```

But we ignore `Display` for now and focus on another use case. That is we want to access the inner value of `Envelope`.

Solving this problem with implementing `AsRef<str>` we would do the following:

```rust
impl AsRef<str> for Envelope {
    fn as_ref(&self) -> &str {
        // here we up-call to the `AsRef<str>` implementation for String
        self.letter.as_ref()
    }
}
```

and with this we need just one little adjustment:

```rust
fn main() {
    let a_letter = Envelope {
        letter: "a poem".to_string()
    };

    println!("this is a letter: {}", a_letter.as_ref());
}
```

Now the rust compiler does not complain anymore.

Again, of course it would be appropriate to implement `Display` for this very `println!` case, but the point is here that we would want to access the inner data as reference. 

[check out the full example on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ff6dd63f6f6bc0d08b20b319397cd087)

## Example: a composed type

I have to admit, take this example with a grain of salt. It's very likely that this kind of usage
would lead to problems with bigger structs. So as a rule of thumb if a struct has more than 2 fields, better not go down this path.

let's look at a simple struct that represents a weight:

```rust
struct Weight {
    weight: f32,
    unit: String
}

impl Weight {
    /// Weight in Tons that is 157.47 stones 
    pub fn from_tons(weight: f32) -> Self {
        Self { weight, unit: "t".to_string() }
    }

    /// Weight in Stones
    pub fn from_stones(weight: f32) -> Self {
        Self { weight, unit: "st".to_string() }
    }
}
```

As you can see we have not given `pub` fields and also no getter accessor functions.

> So how we can actually get our hand on the data inside?

You've guessed it, `AsRef` is our friend here as well.

```rust
impl AsRef<str> for Weight {
    fn as_ref(&self) -> &str {
        &self.unit
    }
}

impl AsRef<f32> for Weight {
    fn as_ref(&self) -> &f32 {
        &self.weight
    }
}
```

So here we use the `AsRef` trait for the types `f32` and `str` to get access to the weight and unit inside the struct.

```rust
fn main() {
    let a_ton = Weight::from_tons(1.3);

    let tons: &f32 = a_ton.as_ref();
    let unit: &str = a_ton.as_ref();

    println!("a weight of {tons} {unit}");
}
```

you could also skip the variable bindings and make it less verbose:

```rust
fn main() {
    let a_ton = Weight::from_tons(1.3);

    println!(
        "a weight of {} {}",
        a_ton.as_ref() as &f32,
        a_ton.as_ref() as &str
    );
}
```

[checkout the full example on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=be1a8d34116766920dda8e6abd082600)

## Wrap up

`AsRef` is a very handy tool and if you go through the standard library, you will find it implemented for a lot of types. 
By this a lot of reference types become interchangeable and this increases the overall ergonomics a lot.

As you also have seen how `AsRef` can be useful to get access to inner data of structs without having to provide accessory methods or public fields.


A very related function to get the inner value of a struct, but as value (not as reference) is the [.into_inner()](https://doc.rust-lang.org/std/convert/trait.Into.html?search=into_inner). 
As you can see it is used a lot in the std lib, but it is not bound to a trait. So this is more of a convention: Whenever you need to consume a struct and unfold the inner wrapped type, then `.into_inner()` is the common schema to go for.

Versions used for this post:

```sh
$ cargo --version && rustc --version
cargo 1.61.0 (a028ae42f 2022-04-29)
rustc 1.61.0 (fe5b13d68 2022-05-18)
```

To receive updates about new blog post and other rust related news you can [follow me on twitter](https://twitter.com/5422m4n).