What are Traits in Rust?

a top down view of a crab, the image is meant to supplement the idea of traits in rust

The folks on the r/rust subreddit would have me and others believe that understanding traits in rust is a simple task. I don’t think I agree.

I think that reciting “Traits are the Rust version of interfaces”, and then writing a trait implementation on a struct is simple, and is some level of understanding of traits.

The note in thebook says:

“Note: Traits are similar to a feature often called interfaces in other languages, although with some differences.”

I believe this note is very helpful, but also ever so slightly misleading for folks who’ve used interfaces ad nauseam.

In C#, Kotlin, and even TypeScript, an interface is a means of declaring mandatory publicly available member and method signatures. The focus is very broad and can be used in a huge number of ways, none of which I’ll cover here.

Traits are different.

But in a good way. A trait isn’t meant to provide a sort of flex-identity to any given struct instance. It’s only meant to describe ‘what an instance can do’. A sort of membership card that doesn’t give any information about the individual, only the behaviors you can expect from them.

In a way, there is a delightfully human and ethical aspect to this as Rust doesn’t care what you are, or where you came from. The fact is that you (the instance) are here now, and I (the compiler) want to know what you can do. Rust's traits are like the book “Anyone can cook” from the movie Ratatouille. A rat may not belong in the kitchen, but if it’s clean, and if it can cook, then why stop it?

That. is. soooo. satisfying.

Traits cannot define properties

And why should they? If a property has a name, it’s possible to create a function called get_name and then test that function. Now the concept of providing a name from a smaller or different context into another one becomes a behavior. And that behavior can be defined in the trait.

At first, I felt this was an affront. I felt handicapped, that the Rust compiler was coating the floor with molasses and preventing me from getting my concepts built.

Then I realized I need to stop seeing traits as interfaces, and instead view them as a way of providing a bit of a resume to all parties involved about exactly what it is that this entity is capable of.

This post is an Index of Experiments

I won’t wax on any further about how enamored I am with Traits. Below are links to some of my posts that cover my discovery process and implementation ideas for traits.

Compose Specialized Instances

Traits can implement their own version of the new function. This was the first thing I did with Traits and found it pretty interesting and useful to be able to init structs to a known (and named) state using traits. Composing Instances Right Where They Are Needed With Rust Traits is a good way to get started with traits.

 

Default Trait Implementations

It’s not always needed or useful to tell the compiler exactly how a trait should be implemented for a specific struct. Sometimes the trait itself does all the work it needs without redefining how it should behave. This sort of utilization is called Default Trait Implementation.

 

Quasi-Fuzzy Function Parameters

With Rust, it’s possible to have a function accept a parameter that is not fully defined. It’s possible to create a function that only evaluates the presence of a given trait for a struct. This makes it so that the compiler cares only about 2 things:

  1. The scope of the defined function will utilize methods described within a given trait or traits.

  2. The inbound parameter must have the specified trait or traits implemented prior to it’s arrival within the function.

I cover this concept in more detail in my post about implementing the concept of Quasi-Fuzzy Function Parameters.

 

Implement Only, and Exactly the Right Methods

I love this concept because it allows for functionality to be given to a struct only when it’s needed. It creates a separation of concerns where the struct simply becomes a data representation, and the traits now describe what needs to be done with that data. This concept is so elegant it feels like the creators or Rust may have somehow achieved ‘art’ with this language feature. This post covers more about implementing traits only in the specific scopes where they are needed.

 

A quick exercise for understanding Trait Bounds

I made this as a reference post for myself to see how I approached understanding utilizing the bounding syntax for Rust traits. It’s a super quick post because I didn’t dive terribly deep but I just wanted to have a surface-level understanding of what am I actually looking at when I see trait bounding syntax.

Conclusion

This post is a sort of indexing post for my work on Traits. I’ll update it over time as I become stronger and grasp a bit more intuitively how they are designed to be used. It’d be very easy to over-engineer solutions using Traits, so I’ll be interested to see what I think about this post in a year’s time.

Previous
Previous

Result Vs. Panic in Rust

Next
Next

An Exercise To Understand Rusts Trait Bounds Syntax