Back to News for Developers

Rust Nibbles - Gazebo: Comparisons

July 27, 2021ByNavyata Bawa

This article was written in collaboration with Bob Yang, a Software Engineer in the Developer Infrastructure organization at Facebook.

The Rust library Gazebo contains a collection of well-tested Rust utilities in the form of standalone modules. In this series of blog posts, we will cover some of the modules that make up the Gazebo library. In today’s blog, we will cover the module cmp. This blog is a part of our Rust Nibbles series, where we go over the various Rust libraries we have open-sourced to learn more about what motivated their creation and how one can use them.

The Gazebo cmp module includes several utilities that make comparing and testing equality easier.

Compare and Equal Chains

Implementing PartialOrd for complex objects could involve a long sequence of comparisons.

fn cmp(&self, other: &Self) -> Ordering {
  match self.field_1.cmp(other.field_1) {
    Ordering::Equal => {},
    ord => { return ord; }
  }

  match self.field_2.cmp(other.field_2) {
    Ordering::Equal => {},
    ord => { return ord; }
  }

  ...
  // repeat for however many fields we need to compare
}

        

The cmp_chain macro allows us to simplify the the above into just a few lines:

fn cmp(&self, other: &Self) -> Ordering {
  cmp_chain!(
    self.field_1.cmp(other.field_1),
    self.field_2.cmp(other.field_2),
    ...
    // repeat for however many fields we need to compare
  )
}
        

It also works with Result and ? in try_cmp

fn try_cmp(&self, other: &Self) -> Result<Ordering, ()> {
  cmp_chain!(
    self.field_1.try_cmp(other.field_1)?,
    self.field_2.try_cmp(other.field_2)?,
    ...
    // repeat for however many fields we need to compare
  )
}
        

eq_chain is the same as cmp_chain, but for equality.

While these comparison and equality implementations will work functionally, they can be tedious to write and maintain. The cmp module steps in to alleviate that work.

PartialEqAny

PartialEqAny is made to allow equality between any types. This is especially useful in allowing equality on traits by having traits return a token that can be used for comparison. PartialEqAny wraps an object that is PartialEq, such that it hides the actual type of the object which then allows equality between any type, but maintains the equality behaviour of that object.

As an example, we can have a trait

trait MyTrait {
  ...
  
  fn eq_token(&self) -> PartialEqAny;
}
        

with several implementations Foo and Bar.

We can derive PartialEq on each respective struct, then provide the implementation

impl MyTrait for Foo {
  ... 
  fn eq_token(&self) -> PartialEqAny {
    PartialEqAny::new(self)
  }
}
        

and the same for Bar. PartialEqAny will take care to only return equal if the underlying object is of the same type, and equal according to the underlying object’s equal.

We can then take any instance of dyn MyTrait and obtain the equality token to compare. It is also possible to implement PartialEq on the dyn trait itself

impl PartialEq for dyn MyTrait {
  ... 
  fn eq(&self, other: &Self) -> bool {
    self.eq_token() == other.eq_token()
  }
}
        

which lets us use the object trait anywhere equality is required.

Note that rather than deriving PartialEq on Foo and Bar, we can create the equality token using certain fields of the two structs, which allows flexible equality behaviours on the trait.

MaybeEq

MaybeEq introduces the concept of an object being maybe PartialEq. Any object that is MaybeEq can be arguments to maybe_eq(x, y) which returns an Option<bool>, where None indicates not equality, and Some indicates equality according to the boolean.

We hope that this blog helps you understand the Cmp module, how to use it and gives you good insight into what it does. Look out for our next blog in this series, where we discuss the cast module, which provides more restrictive alternatives for unsafe conversions.

Be sure to check out our previous blogs in the Gazebo series to learn more about the various features the Gazebo library has to offer -
Gazebo - Prelude
Gazebo - Dupe
Gazebo - Variants
Gazebo - AnyLifetime

About the Rust Nibbles series

We at Facebook believe that Rust is an outstanding language that shines in critical issues such as memory safety, performance and reliability. We joined the Rust Foundation to help contribute towards the growth, advancement and adoption of Rust, and towards sustainable development of open source technologies and developer communities across the world.

This blog is a part of our Rust Nibbles series, where we go over the various Rust libraries we have open-sourced to learn more about what motivated their creation and how one can use them. We hope that this series helps you create amazing projects by using these libraries and encourages you to try them out.

To learn more about Facebook Open Source, visit our open source site, subscribe to our YouTube channel, or follow us on Twitter and Facebook.