Quasi-Fuzzy Function Parameters Using Rust Traits

handwriting that is a function signature named a_bit_fuzzy which accepts unknown parameters, and returns "your relevant function result"

Accepting different types in function parameters with Rust?

I created a little task for myself to try to understand more about how I can use the compiler to my advantage rather than feel hampered by it when it stops me from programming in my normal TypeScripty-C-Sharpy-Pythonic way.

My mission is to create 3 different structs which have a unique set of properties, some of which overlap.

I want to have a function that can accept different parameters. I feel that having a function that is meant to handle variability is a software concept worth discussing all on its own.

First, let’s create 3 very basic structs.

I’m going to create something like game characters because it’s something that a game-state-manager sort of entity might need to manage, but also it’s pretty simple as an analogy.

These three structs are different to show that we can pass structs of different types into the same function. But they are similar enough that a single function has a reason to be shared among them.

Here’s where I got stuck

a set of parentheses with "T?" inside, and underneath it says "implements trait XYZ" and then the function can return an i32

A struct cannot be passed into a function with an impl as the defined parameter, unless that trait has already been implemented for that struct.

I simply could not wrap my head around the clever way that Rust dispels ambiguity when accepting a parameter that isn’t explicitly defined in the parameter type definition.

In the function above it looks like we’re accepting the actual trait called HPHandling. And inside the function, we can see that we’re invoking a method on a specific struct instance.

Except we’re not.

GOD this took me forever to understand, more than an hour. Maybe more than 2. The compiler is being told that it doesn’t need to care what instance it receives, only that it will be receiving some instance that already has a specific trait implemented for it.

It’s bulletproof because the struct cannot be passed into this function unless it’s already had the trait implemented. And the compiler checks the plausibility of trait implementation at compile time.

Chefs. Kiss.

It hurts sometimes to know that I will never be as clever as the folks who created Rust, but at least I possess the faculties to appreciate their work.

To sum up again with a nice quote:

Functions that use traits as parameters do not care what instance come into their scope, they only care about what those instances can do. I.E. do those instances have the appropriate trait(s) implemented.

Let’s see it in action

Here is a gist where I get the hp of a dog struct in a test.

In the test, the trait for the dog gets implemented, and then we can pass a reference of the dog struct directly into the get_hp function.

Because the get_up function is set up to accept any struct which has the trait HPHandling, dog is given the OK and we’re able to see that in the test it does return the hp value assigned to the dog.

Conclusion

Functions in Rust can dynamically handle different types, so long as they implement the same trait. Note only items which are relevant to the trait can be invoked or accessed from within the scope of the function. But that’s kind of the point right?

This journey was well worth my time. I definitely feel like this changed my outlook on how to validate entities. I don’t write Rust professionally so it’s something I’ll still need to worry about unfortunately.

But it’s really interesting to think about it from the perspective of, “The compiler or interpreter should only care about precisely the scope of its interactions on a given instance.” This is a heroic level of adherence to the SOLID principles, and I think I have some ideas about improving my code across disciplines now having learned this.

Previous
Previous

Implement Only and Exactly The Needed Methods

Next
Next

What is a Default Trait Implementation in Rust?