Methods that take references to Type Parameter

I am toying with this problem, I wrote some simple code below that will not compile:

struct MyType<T>
    where T: PartialEq,
{
    data: T,
}

impl<T> MyType<T>
    where T: PartialEq,
{
    fn write(&mut self, data: T) {
        self.data = data;
    }

    fn matches(&self, data: T) -> bool {
        data == self.data
    }
}

fn main() {
    let foo: MyType<String> = MyType { data: String::from("ABC") };
    let bar = String::from("XYZ");

    if foo.matches(bar) {
         println!("{:?}", bar);
    }    
}

The error is simple enough, we've moved bar into the matches() method:

error[E0382]: borrow of moved value: `bar`
  --> src/main.rs:26:26
   |
23 |     let bar = String::from("XYZ");
   |         --- move occurs because `bar` has type `std::string::String`, which does not implement the `Copy` trait
24 | 
25 |     if foo.matches(bar) {
   |                    --- value moved here
26 |         println!("{:?}", bar);
   |                          ^^^ value borrowed here after move

A first attempt to rewrite the matches() method to compare a borrowed value (simply using data: &T) doesn't compile:

error[E0277]: can't compare `&T` with `T`
  --> src/main.rs:17:14
   |
17 |         data == self.data
   |              ^^ no implementation for `&T == T`
   |
   = help: the trait `std::cmp::PartialEq<T>` is not implemented for `&T`
   = help: consider adding a `where &T: std::cmp::PartialEq<T>` bound

What I am confused by now is how to specify the trait bounds. I tried the compiler's suggestion and hit lifetime issues:

error[E0637]: `&` without an explicit lifetime name cannot be used here
 --> src/main.rs:2:25
  |
2 |     where T: PartialEq, &T: PartialEq
  |                         ^ explicit lifetime name needed here

error[E0310]: the parameter type `T` may not live long enough
 --> src/main.rs:1:1
  |
1 |   struct MyType<T>
  |   ^             - help: consider adding an explicit lifetime bound `T: 'static`...
  |  _|
  | |
2 | |     where T: PartialEq, &T: PartialEq
3 | | {
4 | |     data: T,
5 | | }
  | |_^
  |
note: ...so that the reference type `&'static T` does not outlive the data it points at
 --> src/main.rs:1:1
  |
1 | / struct MyType<T>
2 | |     where T: PartialEq, &T: PartialEq
3 | | {
4 | |     data: T,
5 | | }
  | |_^

I'd like to tell the compiler "This lifetime is only needed in the matches function and isn't related to the lifetime of the data field".

Luckily the PartialEq::eq only takes the arguments by reference, so just keep the impl as it was originally and call it like this:

PartialEq::eq(data, &self.data)

As for the direct question, you should look into where for<'a> clauses.

1 Like

You can also just do data == &self.data because if you have T: PartialEq<T>, then you will get &T: PartialEq<&T> for free

3 Likes

Has led me here: https://doc.rust-lang.org/nomicon/hrtb.html

I think you and

have it right, thank you both (again) :slight_smile:

1 Like

Here's a playground link with the simple code and the simple (once you know how) fix :slight_smile:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5f2bcf955dc3b6bb0acae06f412701a0

Sometimes worth remembering the compiler might not suggest the best solution to your specific problem :smiley:

By the way, are we able to special-case the T == &T error (since it looks rather common and at the same time specific) to suggest taking a borrow instead of adding the trait bound? Does it worth the RFC?

1 Like

Funnily enough I filed https://github.com/rust-lang/rust/issues/67571 today, that should cover this. Could you add a comment to it pointing at this thread and explaining the case you hit? The compiler should certainly be guiding you in the right direction here.

1 Like

Done, awesome stuff: go team! :slight_smile:

I'll be able to add more context if needed sooner or later, this is especially written for the forum post and not what was encountered in the wild exactly (the crucial detail is a comparison of T and &T still)

Still, the messiness of the real encounter might help test any potential solution

1 Like