It's easy to create pathological types like the one below
struct Rec<T> {
item: T,
rec: Option<Box<Rec<Rec<T>>>>,
}
fn main() {
let e: Option<Rec<usize>> = None;
}
that fails to compile (Playground) with the error message below
error[E0320]: overflow while adding drop-check rules for std::option::Option<Rec<usize>>
--> src/main.rs:7:9
|
7 | let e: Option<Rec<usize>> = None;
| ^
|
= note: overflowed on Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<Rec<usize>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
However, there's a workaround: replacing Box<T> with Box<dyn The<Type=T>> where The is a trait defined as follows:
trait The {
type Type;
fn self_ref(&self) -> &Self::Type;
fn self_mut(&mut self) -> &mut Self::Type;
fn self_own(self) -> Self::Type;
}
impl<T> The for T {
type Type = Self;
fn self_ref(&self) -> &Self {
self
}
fn self_mut(&mut self) -> &mut Self {
self
}
fn self_own(self) -> Self {
self
}
}
struct Rec<T> {
item: T,
rec: Option<Box<dyn The<Type=Rec<Rec<T>>>>>,
}
fn main() {
let e: Option<Rec<usize>> = None;
}
and then the program compiles without errors. (Playground)
As far as I can tell, Box<T> and Box<dyn The<Type=T>> are functionally equivalent, except that the second one requires an additional method call to get at the T. In particular, I don't think they should behave differently with respect to lifetimes and dropping.
So here are my questions:
- Is there any situation where
Box<dyn The<Type=T>>cannot be used as a replacement forBox<T>? - Why does drop-checking not overflow with the trait?
- Is this sound? If not, too bad, if yes, could the drop-checking rule be rewritten to treat the two situations the same?