Given a type like dyn Trait<W> + 'w
, I am thoroughly convinced that there is no reason for the compiler to require that 'w
contains all of the lifetime information in Self
in cases where this information is already contained in W
.
/// The compiler will guarantee that this type cannot outlive any
/// lifetime contained in W, because this type is invariant in W.
///
/// When we construct it unsafely in hide_lifetime(), we use W = Self.
pub type ImpliedLifetime<W> = Box<dyn Trait<W> + 'static>;
pub fn hide_lifetime<W: Trait<W>>(w: W) -> ImpliedLifetime<W> {
unsafe {
std::mem::transmute::<
Box<dyn Trait<W> + '_>,
Box<dyn Trait<W> + 'static>,
>(Box::new(w))
}
}
pub trait Trait<W> { fn method(&self); }
// this blanket impl was chosen to give you many types that
// do implement the trait bound, and many types that don't.
impl<W: ToString> Trait<W> for W {
fn method(&self) { println!("{}", self.to_string()); }
}
I challenge anyone to invoke undefined behavior with this function. You're free to add any amount of safe code, including arbitrary trait impls. But you cannot change the code above. Here is a playground to get you started.
Why do I care? A hack like this is the only way I can achieve the perfect signatures I want for writing an NPY file, which only conditionally requires a Seek
bound:
// simplified signatures; in the link above, these are Builder::begin_1d
// and Builder::begin_nd
impl<W: Write> NpyWriter<W> {
pub fn new_nd(w: W, shape: &[usize]) -> NpyWriter<W>;
pub fn new_1d(w: W) -> NpyWriter<W> where W: Seek;
}
Without this, I have to settle for new_nd() -> NpyWriter<PanicSeek<W>>
or new_1d() -> NpyWriter<'w, W> where W: 'w