One possibility, is we could have an Unbound
auto trait (idk how viable this is, though) that is unimplemented for raw pointers (for the same reason raw pointers don't implement either Send or Sync) and fn-ptrs (then reimplemented for Box etc.), so you could have a safe Deref{,Mut,Move}
for Lt<'a>
that implements Bound
. Formally:
/// SAFETY:
/// A type T that explicitly implements this trait warants that it either does not contain non-lifetime bound pointers (including non-'static pointers) to non-'static data, or that it otherwise enforces the non-'static lifetime
#[lang="unbound_trait"] // Add fn-ptr impls
pub unsafe auto trait Unbound{}
impl<T: ?Sized> !Unbound for *mut T{}
impl<T: ?Sized> !Unbound for *const T{}
// Added by lang-item above
impl<Args..., R> !Unbound for unsafe? extern<abi> fn(Args...)->R{}
// SAFETY: Lifetime of the reference is enforced by its own type
unsafe impl<'a,T: ?Sized> Unbound for &'a T{}
unsafe impl<'a,T: ?Sized> Unbound for &'a mut T{}
// SAFETY: The Lt<'a> type enforces the lifetime and does not allow safe access to an inner value that isn't Unbound
unsafe impl<'a,T: ?Sized> Unbound for Lt<'a,T>{}
unsafe impl<T: ?Sized+Unbound,A: Unbound> Unbound for Box<T,A>{} // Vec<T>/String/etc.
unsafe impl<T: ?Sized+Unbound, A: Unbound> Unbound for Rc<T,A>{} // Arc<T,A>/etc.
#[repr(transparent)] // Transparent over T
pub struct Lt<'a,T: ?Sized>(PhantomData<&'a ()>,T); // T needs to trail the type to be `?Sized`
impl<'a,T> Lt<'a,T>{
pub fn new(x: T) -> Self;
pub fn into_inner(self) -> T where T: Unbound;
/// Safety:
/// Unless T: Unbound, it is only guaranteed that it and any pointers it contains will be valid for up to 'a - using it outside that lifetime may cause undefined behaviour
pub unsafe fn into_inner_unchecked(self) -> T;
pub fn upgrade(self) -> Lt<'static,T> where T: Unbound;
}
impl<T> Lt<'static,T>{
pub fn into_inner_unbounded(self) -> T;
}
impl<'a, T: ?Sized> Lt<'a,T>{
pub fn get(&self) -> &T where T: Unbound;
pub fn get_mut(&mut self) -> &mut T where T: Unbound;
pub unsafe fn get_unchecked(&self) -> &T;
pub unsafe fn get_mut_unchecked(&mut self) -> &mut T;
// Other methods
}
impl<'a,Args...,R> FnOnce<(Args...)> for Lt<'a,extern<abi> fn(Args...)->R>{}
impl<'a,Args...,R> FnMut<(Args...)> for Lt<'a,extern<abi> fn(Args...)->R>{}
impl<'a,Args...,R> Fn<(Args...)> for Lt<'a,extern<abi> fn(Args...)->R>{}
impl<'a,Args...,R> UnsafeFnOnce<(Args...)> for Lt<'a,unsafe extern<abi> fn(Args...)->R>{}
impl<'a,Args...,R> UnsafeFnMut<(Args...)> for Lt<'a,unsafe extern<abi> fn(Args...)->R>{}
impl<'a,Args...,R> UnsafeFn<(Args...)> for Lt<'a,unsafe extern<abi> fn(Args...)->R>{}
Basically, Lt
(which possibly should be Bounded
or something else to be bikeshedded rather than the rather short, terse, and ambiguous Lt
) would act like Pin
, in that it only lets you access the inner value if it implements an auto trait. unsafe code that manages lifetimes in special ways could then return an Lt<'a,fn-ptr>
and know that it is safe to move arround for 'a, without exposing the otherwise 'static bound fn-ptr to safe code that could use/call it outside of 'a.