pointersgenericsrustcopy

Rust, Copy Trait not working with a type when it uses generic


Rust won't compile the following:

#[derive(Copy, Clone)]
struct WhateverStruct<T> {
    field : T
}

trait WhateverStructTrait<T> {
    fn to(self) -> WhateverStruct<T>;
}
impl<T> WhateverStructTrait<T> for *mut WhateverStruct<T> {
    fn to(self) -> WhateverStruct<T> {
        unsafe {* self }
    }
}

fn test() {
    let x = WhateverStruct { field : 7u32 };
    let _y = x;
    let _z = x;
    println!("Copying is fine");
}

fn main() {
    test();
}

It complains on the unsafe {* self } part saying that

*self has type WhateverStruct<T>, which does not implement the Copy trait

However, it quite clearly does implement the copy trait. The test function has no errors. If you change struct WhateverStruct<T> { field : T } into struct WhateverStruct { field : u32 } and remove the <T> from the rest of the code, everything compiles and runs just fine. So Rust isn't liking the generic.

Here you can see on the playground : https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5d5c3a0f0e6e0153ec286ce38f0f3a2d

Is this a bug? Is there a work around?


Solution

  • For any generic type Foo<T> when you derive Copy and Clone, the traits always have T bound to be Copy or Clone respectively. Sometimes, though you don't actually need T to implement those traits for the struct as a whole to be Copy or Clone.

    An example of this is the following struct.

    #[derive(Copy, Clone)]
    struct Foo<T> {
        _marker: std::marker::PhantomData<T>,
    }
    

    If we look at the code that the derive generates (cargo-expand works for that purpose) we get something like

    use std::prelude::v1::*;
    #[macro_use]
    extern crate std;
    
    struct Foo<T> {
        _marker: std::marker::PhantomData<T>,
    }
    
    #[automatically_derived]
    #[allow(unused_qualifications)]
    impl<T: ::core::marker::Copy> ::core::marker::Copy for Foo<T> {}
    
    #[automatically_derived]
    #[allow(unused_qualifications)]
    impl<T: ::core::clone::Clone> ::core::clone::Clone for Foo<T> {
        #[inline]
        fn clone(&self) -> Foo<T> {
            match *self {
                Foo {
                    _marker: ref __self_0_0,
                } => Foo {
                    _marker: ::core::clone::Clone::clone(&(*__self_0_0)),
                },
            }
        }
    }
    

    Looking at just the implementation of Copy (and cleaning up things a bit) it's

    impl<T: Copy> Copy for Foo<T> {}
    

    So even though Foo<T> doesn't need T to be Copy, it restricts it anyway.

    In these cases, you'll want to simply implement Copy and Clone yourself. There's a fairly trivial implementation that works as long as the actual fields of the struct are Copy.

    struct Foo<T> {
        _marker: std::marker::PhantomData<T>,
    }
    
    impl<T> Copy for Foo<T> {}
    
    impl<T> Clone for Foo<T> {
        fn clone(&self) -> Self {
            *self
        }
    }