Can't specialize Drop
The Drop trait has checks to ensure that the impl can't add new restrictions on generic parameters.
struct S<T> { ... }
impl<T: Foo> Drop for S<T> { ... }
The above won't compile (as intended?), as the S would have a Drop that's conditional based on the type parameter.
However with specialization we could have something like:
struct S<T> { ... }
default impl<T> Drop for S<T> { ... }
impl <T: Foo> Drop S<T> { ... }
Both of these examples yield the same error
error[E0367]: The requirement `T: Foo` is added only by the Drop impl.
My first instinct was that this had something to do with the type information getting lost on destructors, but that doesn't seem to be the case as the issue can be worked around by specializing a different trait, to which drop delegates to:
struct S<T> { ... }
// Specialized Drop implementation.
trait SpecialDrop { fn drop( &mut self ); }
default impl<T> SpecialDrop for S<T> { ... }
impl<T: Foo> SpecialDrop for S<T> { ... }
// Drop delegates to SpecialDrop.
impl<T> Drop S<T> { fn drop(&mut self) {
(self as &mut SpecialDrop).drop()
}
Linking to #31844
Basically we should skip this check entirely if there's a default impl that covers this one (and rely on the check giving an error on the default impl itself if there's a problem there). Unsure if there's an easy, efficient way to fetch the list of impls at that stage.
Manish Goregaokar at 2017-12-22 11:00:21
Someone in the Rust Community Discord just got
error[E0366]: Drop impls cannot be specializedand then we found this issue.As a clarification: does this issue and especially the comment by @Manishearth imply that specialized Drop is a thing that could be done and just hasn't been implemented/discussed/designed, or will specialized Drop always be impossible (and is there an explanation somewhere as to why)?
Félix Saparelli at 2020-02-17 22:39:11
Yes, it could be done. That said, in general Drop is already so special-cased in the type system this does sound like a recipe for bugs.
Manish Goregaokar at 2020-02-18 05:10:13
(Disregard this if it's inappropriate)
This would be nice, without specialization, for RAII/scoped/transaction guards. Mostly because of generic churn:
// horrible, look at those Deref(Mut)! struct SubtreeHelper<'r, 's, P: Borrow<str> + Ord, O: Borrow<str> + Ord, T: PatternTypes> where Self: 'r { root: &'r mut Parser<'s, P, O, T>, } impl<'r, 's, P: Borrow<str> + Ord, O: Borrow<str> + Ord, T: PatternTypes> SubtreeHelper<'r, 's, P, O, T> where Self: 'r { fn start(value: &'r mut Parser<'s, P, O, T>) -> Self { value.consts.protos.push(Default::default()); Self { root: value, } } fn commit(mut self) -> usize { let mut self_ = ManuallyDrop::new(self); let proto = self_.root.consts.protos.pop().unwrap(); let id = self_.root.closed_subtrees.next().unwrap(); self_.root.consts.protos.insert(id, proto); id } } impl<'r, 's, P: Borrow<str> + Ord, O: Borrow<str> + Ord, T: PatternTypes> std::ops::Deref for SubtreeHelper<'r, 's, P, O, T> where Self: 'r { type Target = Parser<'s, P, O, T>; fn deref(&self) -> &Self::Target { &*self.root } } impl<'r, 's, P: Borrow<str> + Ord, O: Borrow<str> + Ord, T: PatternTypes> std::ops::DerefMut for SubtreeHelper<'r, 's, P, O, T> where Self: 'r { fn deref_mut(&mut self) -> &mut Self::Target { self.root } } impl<'r, 's, P: Borrow<str> + Ord, O: Borrow<str> + Ord, T: PatternTypes> Drop for SubtreeHelper<'r, 's, P, O, T> where Self: 'r { fn drop(&mut self) { // remove "partial" proto self.root.consts.protos.pop().expect("SubtreeHelper"); } }// cleaner? struct SubtreeHelper<'r, T> where Self: 'r { root: &'r mut T, } impl<'r, 's, P: Borrow<str> + Ord, O: Borrow<str> + Ord, T: PatternTypes> SubtreeHelper<'r, Parser<'s, P, O, T>> where Self: 'r { fn start(value: &'r mut Parser<'s, P, O, T>) -> Self { value.consts.protos.push(Default::default()); Self { root: value, } } fn commit(mut self) -> usize { let mut self_ = ManuallyDrop::new(self); let proto = self_.root.consts.protos.pop().unwrap(); let id = self_.root.closed_subtrees.next().unwrap(); self_.root.consts.protos.insert(id, proto); id } } impl<'r, T> std::ops::Deref for SubtreeHelper<'r, T> where Self: 'r { type Target = T; fn deref(&self) -> &Self::Target { &*self.root } } impl<'r, T> std::ops::DerefMut for SubtreeHelper<'r, T> where Self: 'r { fn deref_mut(&mut self) -> &mut Self::Target { self.root } } impl<'r, 's, P: Borrow<str> + Ord, O: Borrow<str> + Ord, T: PatternTypes> Drop for SubtreeHelper<'r, Parser<'s, P, O, T>> where Self: 'r { fn drop(&mut self) { // remove "partial" proto self.root.consts.protos.pop().expect("SubtreeHelper"); } }Soni L. at 2021-02-06 00:46:01