Rust thinks I'm letting a reference escape

error[E0521]: borrowed data escapes outside of method
  --> src/event.rs:21:9
   |
20 |     fn add_assign(&mut self, rhs: &dyn Fn(Sig)->()) {
   |                              ---  - let's call the lifetime of this reference `'1`
   |                              |
   |                              `rhs` is a reference that is only valid in the method body
21 |         self.listeners.push(MutRc::new(rhs));
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         `rhs` escapes the method body here
   |         argument requires that `'1` must outlive `'static`
   |
   = note: requirement occurs because of a mutable reference to `Vec<MutRc<dyn Fn(Sig)>>`
   = note: mutable references are invariant over their type parameter
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

Its not escaping because my MutRc<T> is a mutable reference counter.

MutRc source
use std::{ alloc::{GlobalAlloc, Layout, System}, any::{Any, TypeId}, ffi::c_void, marker::PhantomData, ops::*, ptr::{self, drop_in_place, null_mut}};

use libc;
struct MRcMeta{
    pub borrows:usize,
    pub copies:usize,
    pub total_copies:usize,
    pub is_valid:bool,
    pub layout:Layout,
    pub typ:TypeId,
    pub vtable:*const usize
}
struct FatPtrPre{
    data:*const u8,
    vtable:*const usize
}
pub struct MutRc<T:?Sized>{
    data:*mut u8,
    metadata:*mut MRcMeta,
    isweak:bool,
    phantom:PhantomData<T>
}
#[derive(Debug)]
pub enum MRCCastErr {
    DifferentType,
    Borrowed,
    Invalid
}
impl<T:?Sized> MutRc<T>{
    pub fn new(obj:&T) -> MutRc<T>{
        unsafe {
            let meta = libc::malloc(size_of::<MRcMeta>()) as *mut MRcMeta;
            let layout = Layout::for_value(obj);
            let typ = layout.type_id();            
            let (objptr,objvt) = std::mem::transmute_copy::<&T,(*const u8,*const usize)>(&obj);

            meta.write(MRcMeta { 
                borrows: 0, 
                copies: 1,
                total_copies:1 , 
                is_valid:true, 
                layout:layout,
                typ:typ,
                vtable:objvt
             });
            let data = System.alloc(layout);
            data.copy_from(objptr, size_of_val(obj));
            MutRc{ data: data, metadata: meta, isweak: false ,phantom:PhantomData::default()}
        }
    }
    pub fn to_weak(&self) -> MutRc<T>{
        unsafe{
            self.meta().total_copies+=1;
        }
        MutRc { data: self.data, metadata: self.metadata, isweak: true ,phantom:PhantomData::default()}
    }
    pub fn to_strong(&self) -> MutRc<T>{
        unsafe{
            let meta = self.meta();
            meta.total_copies+=1;
            meta.copies+=1;
        }
        MutRc { data: self.data, metadata: self.metadata, isweak: false ,phantom:PhantomData::default()}
    }
    unsafe fn to_fat(&self) -> *mut T{
        unsafe{
            std::mem::transmute_copy::<FatPtrPre,*mut T>(&FatPtrPre{
                data:self.data,
                vtable:self.meta().vtable
            })
        }
    }
    unsafe fn access(&self)-> Option<&mut T>{
        unsafe {
            if self.meta().is_valid {
                self.to_fat().as_mut()
            } else { None }
        }
    }
    unsafe fn meta(&self) -> &mut MRcMeta{
        unsafe{self.metadata.as_mut()}.unwrap()
    }
    pub fn as_ref(&self) -> MutRcRef<T>{
        unsafe{self.meta().borrows+=1;}
        MutRcRef::new(self, false)
    }
    pub fn as_mut(&self) -> Option<MutRcRef<T>>{
        unsafe {
            let meta = self.meta();
            if meta.borrows != 0 {
                return None
            }
            meta.borrows+=1;
            Some(MutRcRef::new(self, true))
        }
    }
    unsafe fn clean(&mut self) {
        unsafe {
            let meta = self.metadata.as_mut().unwrap();
            if self.data.is_null() || !meta.is_valid{
                return;
            }
            meta.total_copies-=1;
            if !self.isweak {
                meta.copies-=1;
                if meta.copies <= 0 {
                    drop_in_place(self.data);
                    libc::free(self.data as *mut c_void);
                    meta.is_valid = false;
                }
            }
            if meta.total_copies <= 0 {
                libc::free(self.metadata as *mut c_void);
                self.metadata = null_mut();
            }
        }
    }
    pub fn is_valid(&self)-> bool{
        unsafe {
            (!self.metadata.is_null()) && self.meta().is_valid && (!self.data.is_null())
        }
    }
    pub fn ptreq_mutrc(&self,other:MutRc<T>) -> bool{
        ptr::addr_eq(self.data,other.data)
    }
    pub fn ptreq_ref(&self,other:&T) -> bool{
        ptr::addr_eq(self.data,other)
    }
    pub fn is_borrowed(&self) -> bool{
        unsafe {
            self.meta().borrows != 0
        }
    }
    pub unsafe fn cast_to_ref<Target:'static>(&self) -> Result<&Target,MRCCastErr>{
        unsafe {
            if self.meta().typ != TypeId::of::<Target>() {
                return Err(MRCCastErr::DifferentType)
            }
            if !self.is_valid() {
                return Err(MRCCastErr::Invalid)
            }
            Ok((self.data as *mut Target).as_ref().unwrap())
        }
    }
    pub unsafe fn cast_to_mut<Target:'static>(&mut self) -> Result<&mut Target,MRCCastErr>{
        unsafe {
            if self.meta().typ != TypeId::of::<Target>() {
                return Err(MRCCastErr::DifferentType)
            }
            if self.is_borrowed() {
                return Err(MRCCastErr::Borrowed)
            }
            if !self.is_valid() {
                return Err(MRCCastErr::Invalid)
            }
            Ok((self.data as *mut Target).as_mut().unwrap())
        }
    }
}
impl<T:?Sized> Clone for MutRc<T>{
    fn clone(&self) -> Self {
        if self.isweak {self.to_weak()} else {self.to_strong()}
    }
    fn clone_from(&mut self, source: &Self) {
        if source.data == self.data && (!source.isweak){
            return;
        }
        unsafe{
            self.clean();
            self.data=source.data;
            self.metadata=source.metadata;
            self.isweak=source.isweak;
            self.meta().total_copies+=1;
            if !self.isweak {
                self.meta().copies+=1;
            }
        }
    }
}

impl<T:?Sized> Drop for MutRc<T>{
    fn drop(&mut self) {
        unsafe {self.clean();}
    }
}

impl<T:?Sized> Deref for MutRc<T>{
    type Target = T;
    fn deref(&self) -> &Self::Target {
        unsafe {self.to_fat().as_ref().unwrap()}
    }
}
impl<T:?Sized> DerefMut for MutRc<T>{
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe{
            if self.meta().borrows != 0 {
                panic!("cannot mutably borrow an already borrowed value")
            }
            self.to_fat().as_mut().unwrap()
        }
    }
}
struct MutRcRef<T:?Sized>{
    data:*mut u8,
    metadata:*mut MRcMeta,
    ismut:bool,
    phantom:PhantomData<T>
}
impl<T:?Sized> MutRcRef<T>{
    fn new(obj:&MutRc<T>,ismut:bool) -> MutRcRef<T>{
        MutRcRef { 
            data: obj.data,
            metadata:obj.metadata,
            ismut: ismut,
            phantom:PhantomData::default()
        }
    }
    unsafe fn to_fat(&self) -> *mut T{
        unsafe{
            std::mem::transmute_copy::<FatPtrPre,*mut T>(&FatPtrPre{
                data:self.data,
                vtable:self.metadata.as_ref().unwrap().vtable
            })
        }
    }
}
impl<T:?Sized> Deref for MutRcRef<T>{
    type Target = T;
    fn deref(&self) -> &Self::Target {
        unsafe {self.to_fat().as_ref().unwrap()}
    }
}
impl<T:?Sized> DerefMut for MutRcRef<T>{
    fn deref_mut(&mut self) -> &mut Self::Target {
        if !self.ismut {
            panic!("is not mutable");
        }
        unsafe {self.to_fat().as_mut().unwrap()}
    }
}
impl<T:?Sized> Clone for MutRcRef<T>{
    fn clone(&self) -> Self {
        if self.ismut {
            panic!("cannot copy a mutable reference")
        }
        unsafe{
            self.metadata.as_mut().unwrap().borrows+=1;
        }
        MutRcRef { data: self.data, metadata: self.metadata, ismut:self.ismut, phantom:PhantomData::default() }
    }
}
impl<T:?Sized> Drop for MutRcRef<T>{
    fn drop(&mut self) {
        unsafe{
            self.metadata.as_mut().unwrap().borrows-=1;
        }
    }
}
1 Like

I don't see listeners or add_assign in the MutRc source, but that code is not sound. Run with Miri under Tools, top right.

let (objptr,objvt) = std::mem::transmute_copy::<&T,(*const u8,*const usize)>(&obj);

This will panic on thin pointers. For wide pointers, the data/metadata order is unspecified / subject to change. So it's unsound even if you fix the immediate problem. There are more involved ways to figure out which half of a wide pointer is the metadata. I can try to dig up one of my old POCs if you'd like.

(I didn't look beyond new.)


I'll take a crack at the error anyway. Is it something like this?

struct Thing<Sig> {
    listeners: Vec<MutRc<dyn Fn(Sig)>>,
}

impl<Sig> Thing<Sig> {
    fn add_assign(&mut self, rhs: &dyn Fn(Sig)->()) {
        self.listeners.push(MutRc::new(rhs))
    }
}

If so the fix is:

    fn add_assign(&mut self, rhs: &(dyn Fn(Sig)->() + 'static)) {

And to explain why, here are some desugarings showing the default trait object lifetimes that were elided.

struct Thing<Sig> {
    //                                     vvvvvvvvv
    listeners: Vec<MutRc<dyn Fn(Sig) -> () + 'static>>,
}

impl<Sig> Thing<Sig> {
    //                                 vv------------------vvvv
    fn add_assign<'d>(&mut self, rhs: &'d (dyn Fn(Sig)->() + 'd)) {
        self.listeners.push(MutRc::new(rhs))
    }
}
2 Likes

&T probably needs T: 'static bound, because even though you're not keeping the top-level reference, the T could contain inner references that are left dangling.

got a new error

error[E0308]: method not compatible with trait
  --> src/event.rs:20:5
   |
20 |     fn add_assign(&mut self, rhs: &(dyn Fn(Sig)->() + 'static)) {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected signature `fn(&mut event::Event<_>, &dyn Fn(Sig))`
              found signature `fn(&mut event::Event<_>, &(dyn Fn(Sig) + 'static))`
note: the anonymous lifetime as defined here...
  --> src/event.rs:19:21
   |
19 | impl<Sig> AddAssign<&dyn Fn(Sig)->()> for Event<Sig>{
   |                     ^
   = note: ...does not necessarily outlive the static lifetime

Well, you need to adjust the trait implementation to match the type of rhs.

 impl<Sig> AddAssign<&(dyn Fn(Sig)->() + 'static)> for Event<Sig>

Alternatively:

struct Thing<'sig, Sig> {
    listeners: Vec<MutRc<dyn Fn(Sig) + 'sig>>,
}

impl<'sig, Sig> Thing<'sig, Sig> {
    fn add_assign(&mut self, rhs: &'sig dyn Fn(Sig)->()) {
        self.listeners.push(MutRc::new(rhs))
    }
}
1 Like

Yeah, now it's catching the error. The dyn Fn could have been a closure that captured short-lived references (stack variables), and you'd make it have dangling pointers.
Adding + 'static to the closure type will forbid it from having temporary references inside.

Sounds like the error message could make it clearer that the issue is not with the lifetime of the reference, but with that of the dyn object.

1 Like