Struct with generic does not work with Copy

#[derive(Debug, Clone, Copy)]
pub struct Tokens<'a, T> {
    pub tokens: &'a [T],
}

#[derive(Debug, Clone)]
pub struct Token {
    pub t: String,
}

fn workOnTokens<'a>(input: Tokens<'a, Token>) -> Tokens<'a, Token> {
    let a = input.tokens.slice(0..2);
    Tokens { tokens: a }
}

fn main() {
    let t = vec![Token {t: String::from("sdasd"),}, Token {t: String::from("11"),},Token {t: String::from("22"),},Token {t: String::from("33"),},];
    let to: Tokens<Token> = Tokens { tokens: &t };
    let b = workOnTokens(to);
    let c = workOnTokens(to);       // <---- this would ends up with `use of moved value
}

while


#[derive(Debug, Clone, Copy)]
pub struct Tokens<'a> {
    pub tokens: &'a [Token],
}

#[derive(Debug, Clone)]
pub struct Token {
    pub t: String,
}

fn workOnTokens<'a>(input: Tokens<'a>) -> Tokens<'a> {
    let a = input.tokens.slice(0..2);
    Tokens { tokens: a }
}

fn main() {
    let t = vec![Token {t: String::from("sdasd"),}, Token {t: String::from("11"),},Token {t: String::from("22"),},Token {t: String::from("33"),},];
    let to = Tokens { tokens: &t };
    let b = workOnTokens(to);
    let c = workOnTokens(to);
}

The first one would got compile error like

error[E0382]: use of moved value: `to`
  --> src/main.rs:47:26
   |
43 |     let to: Tokens<Token> = Tokens { tokens: &t };
   |         -- move occurs because `to` has type `Tokens<'_, Token>`, which does not implement the `Copy` trait
44 | 
45 |     let b = workOnTokens(to);
   |                          -- value moved here
46 | 
47 |     let c = workOnTokens(to);
   |                          ^^ value used here after move

The second works fine without use of moved value. Why is that?

The problem you have here is the behavior of derive, not necessarily with Copy itself.

The derived implementations always put a rather straightforward bound on generic types.

If you derive some (standard library) trait Foo on a type Bar<A, B, C>, then the implementation is always

impl<A, B, C> Foo for Bar<A, B, C>
where
    A: Foo,
    B: Foo,
    C: Foo,

regardless of how A/B/C are used for the fields of Bar.

One rationale for this is that this way your trait implementations do not accidentally leak implementation details (i.e. the types of the fields you use) and it’s harder to accidentally introduce breaking changes when modifying the types of (private) fields.

So in your struct, the field is of type &'a [T]; (shared/immutable) references are always implementing Copy, regardless of the (target) type though, so the derived Copy implementation, which will be of the form impl<'a, T> Copy for Tokens<'a, T> where T: Copy {}, is not as general as it could be. The where T: Copy constrained could be dropped.

The correct workaround in this case is to write the intended, more general, trait implementation manually:

#[derive(Debug)]
pub struct Tokens<'a, T> {
    pub tokens: &'a [T],
}

impl<'a, T> Copy for Tokens<'a, T> {}
impl<'a, T> Clone for Tokens<'a, T> {
    fn clone(&self) -> Self {
        *self
    }
}

(β†’ full adaptation of your code in the playground)

5 Likes

Thanks very much. Your explanation is very clear and helpful! I really appreciate it.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.