Specifically, Rc and Arc are specialized to check pointer equality before calling Eq::eq when the contained T is Eq. This is correct when Eq is implemented correctly, but because Eq is a safe trait, it doesn't have to be implemented correctly.
Obviously, this can't be used (directly) for unsoundness, so it's not an unsound specialization. However, I seem to recall picking up a semi-formal rule that we aren't supposed to be using specialization to achieve results that strictly require specialization while it's still unstable, and this is a case where you can stably observe that stdlib is doing specialization.
use std::fmt::Debug;
struct NoisyClone<T>(T);
impl<T: Clone + Debug> Clone for NoisyClone<T> {
fn clone(&self) -> Self {
println!("before {:?}", self.0);
let x = NoisyClone(self.0.clone());
println!("after {:?}", x.0);
x
}
}
// Try removing this and see the difference
impl <T: Copy + Debug> Copy for NoisyClone<T> {}
fn main() {
let x = [NoisyClone(1), NoisyClone(2)];
let _ = x.clone();
let mut y = [NoisyClone(0), NoisyClone(0)];
y.clone_from_slice(&x);
let v = vec![NoisyClone(3), NoisyClone(4)];
let _ = v.clone();
}
Incidentally, something I learned recently: both the Copy and Eq specializations are technically unsound – in the sense that types can be copied even if they're not Copy, or assumed reflexive even if they're not Eq. However, this only happens when a type is conditionally Copy or Eq depending on lifetimes, which is a pattern that doesn't really occur, so the practical consequences are limited.
I just realized that things like the array’s Clone implementation specializing on Copy can be used to specialize on Copy in third-party crates (in stable rust), too, e.g. with a helper function like this
Cool trick! I'd pondered using it to make a clone_or_copy method that used Copy where possible, but hadn't gone the extra mile to getting specialization from it.
I think you can simplify it a bit to avoid the Cell:
Actually, I might even use this in core. Because core won't let you specialize on Copy without already having the Clone bound, but this would let me do that as a runtime check!
FWIW, your version crashes on the playground with is_copy::<[bool; 1_000_000_000]>() -- SIGKILL, maybe OOM? -- but that works with PhantomData. Not that this is a practical type, but if you do add anything like this to core, take care.
It looks like you can do this with any trait actually. Unfortunately, it seems you can only check if the type implements such trait, but you may not use it (because Copy and Eq have no methods).
That's not unfortunate, that's actually very fortunate because using any methods in specialized implementations still runs into lots of unsoundness in the current state of the specialization feature.
Fortunate or unfortunate, it's not a coincidence. In the past, certain uses of specialization in the standard library have had to be modified – either removed, or cut down to an approved list of types – precisely because they could call methods on the trait being specialized on, and therefore were unsound.