Blog

Phillip England_

Rust Blanket Implementations

extending core types in rust

12/20/2024

Sandbox

The sandbox for this article can be found
here.

My conversation with ChatGPT regarding the topics can be found
here.

Discovery

I wanted to get started working on a simple string parsing library in Rust to
get my feet wet, and to build a low-level primitive tool I can bring with me
into future projects.

I basically want something that will allow me to do quick string operations I
find myself performing frequently.

Here is a tiny Go implementation of
the sort of thing I am discussing.

Just some quick and dirty functions.

Rust is Different

Upon researching how to lay out the library and the best methods to ensure my
users can work with both String and &str, I came across the idea of
blanket implementations.

In Go, it made sense to use functions because Go only has one string type. But
since Rust has multiple string types, we need to think about how to incorporate
multiple types in our library.

I thought to myself, "Can I just create a trait and apply it to both String
and &str?"

The answer is, yes, using blanket implementations.

Compare to Javascript

I think looking at how Javascript and Rust handle this idea is important as the
Javascript example is a bit more easy to digest, but it has the same underlying
idea.

In Javascript, let's imagine we want to take some object and extend it. Well, we
could do something like this in the browser:

1window.hello = () => {
2  console.log("hello from the window");
3};

Then, later on in our program, we may call the method like:

1window.hello();
2// hello from the window

Javascript is prototype-based and allows you to directly mutate core objects in
the language.

Rust does not provide us the ability to do this. So, in Rust, instead of
directly altering core types, we can extend them using traits.

Traits as Extensions

Traits can be implemented on types in a unique variety of ways.

NOTE TO SELF: Here I am again, discussing traits in Rust. Of all the features
I've come across, traits seem to be one of the most fundamental building blocks
of Rust.

Let's first start by defining a trait we wish to implement on &str and
String:

1pub trait SomeExtension {
2    fn shout(&self) -> String;
3}

The AsRef Trait

Rust has a trait named, AsRef which can be used to implement other traits on
multiple core types at once.

Before we dive into how to use AsRef to implement SomeExtension on both
&str and String, we should take a closer look at how AsRef actually works.

You can find the docs
for AsRef here along with a
page
that works through the AsRef<T> trait versus the Borrow trait.

One of the first things I've noticed is how the Rust standard library has a
whole module called convert which might be useful to take a deeper dive into
later. It might be a good idea to just study the standard library and see how
the core Rust team codes their own data types.

NOTE TO SELF: Did you take the time to study the Rust standard library?

Implementing Our Extension

Now that we know AsRef can be used to implement traits on multiple types in a
generic way, we can use it to implement SomeExtension on both &str and
String types.

To implement SomeExtension, write:

1impl<T> SomeExtension for T
2where
3    T: AsRef<str>,
4{
5    fn shout(&self) -> String {
6        let s = self.as_ref();
7        format!("{}!", s.to_uppercase())
8    }
9}

Then, we can use our extension like so:

1fn main() {
2    let some_str = "i am going to shout";
3    let shout = some_str.shout();
4    let some_string = String::from("i also shout");
5    let shout2 = some_string.shout();
6    println!("{} - {}", shout, shout2);
7}

As you can see, &str and String can now use the shout method from the
MyExtension trait.

Conclusion

Again, I am finding myself in a place where I feel the need to really dive deep
into traits in Rust. They keep coming up and their generic syntax is one of the
most challenging things about reading the language and getting a good mental
model of it.

I am thinking I skim through The Book over
the next few days and just get some more general exposure to Rust concepts and
to get better at looking at the code.