I’m using Rust and its trait system to build out a large library. The idea: leave room for testing out multiple implementations and get some succinct contracts. This will give me the ability to A/B test and benchmark different parts as the library grows. The first round of implementations will use the simplest data types (read: Vecs with linear scans).

Now I’m running in to issues with the type checker and PartialEq and I think my use of traits has something to do with it. I was hoping #[derive(PartialEq)] would suffice but I’m missing something.

Let’s back up and look at the trait code:

// history_cache_trait.rs
pub use super::super::super::common_types::*;

pub type ErrorStr = &'static str;
pub type HistoryCacheResult = Result<(),ErrorStr>;

pub trait HistoryCacheTrait {
    fn new() -> Self;
    fn add_change(&mut self, change: CacheChange) -> HistoryCacheResult;
    fn remove_change(&mut self, change: CacheChange) -> HistoryCacheResult;
    fn get_change();
    fn get_seq_num_min();
    fn get_seq_num_max();
}

The code above is used for tracking state changes. I imagine it will grow to become one of the more complex bits of my library. A cache change is a wrapper around a buffer with a simple definition:

#[derive(PartialEq)]
pub struct CacheChange {
    kind: ChangeKind,
    writer_guid: Guid,
    instance_handle: InstanceHandle,
    sequence_number: SequenceNumber,
    data: Vec<u8>
}

The first implementer makes heavy use of the unimplemented!() macro to side step actual implementation but here’s the suspicious implentation:

// history_cache/mod.rs
use std::default::Default;

use super::super::common_types::*;
use super::{ HistoryCacheTrait, HistoryCacheResult };

#[derive(Default)]
pub struct HistoryCache {
    changes: Vec<CacheChange>
}

impl HistoryCacheTrait for HistoryCache {
    // SNIP
    fn remove_change(&mut self, change: CacheChange) -> HistoryCacheResult {
        for c in &self.changes {
            if c == change {

            }
        }

        unimplemented!()
    }
    // SNIP
}

when compiling the code I get this error (courtesy of new formatting in Rust nightly):

   Compiling rtps v0.1.0 (file:///Users/xavierlange/code/dds/rtps)
error[E0277]: the trait bound `&common_types::cache_change::CacheChange: std::cmp::PartialEq<common_types::cache_change::CacheChange>` is not satisfied
  --> src/entity/history_cache/mod.rs:23:16
   |
23 |             if c == change {
   |                ^^^^^^^^^^^
   |
   = help: the following implementations were found:
   = help:   <common_types::cache_change::CacheChange as std::cmp::PartialEq>

I added the #[derive(PartialEq)] to CacheChange and even then I’m using the concrete type CacheChange and not some interface. I am using the PartialEq inside of the HistoryCache’s HistoryCacheTrait impl. Is that the issue? The one interesting bit I see in the error is &common_types::cache_change::CacheChange (focus on the ampersand) – looks like a borrow is perhaps messing things up?

It can get confusing to read the error. I’m pretty confident it’s not the #[derive(PartialEq)] causing the issue. And I have read code which derefs (*) values. And I think I read somewhere that for makes an iterator which borrows values. That would explain the & in the error! Let’s try this definition:

    fn remove_change(&mut self, change: CacheChange) -> HistoryCacheResult {
        for c in &self.changes {
            if *c == change {

            }
        }

        unimplemented!()
    }

Now we have success:

   Compiling rtps v0.1.0 (file:///Users/xavierlange/code/dds/rtps)
    Finished debug [unoptimized + debuginfo] target(s) in 0.90 secs

My rust-fu went up a little bit by putting together all the pieces. Hope this post saves you some time when you hit your next trait bound issue (hint: make sure you and error text agree about the borrow state of the variable!).