Skip to content

__init and __pin_init don't know the lifetime of the created object #26

Open
@bonzini

Description

@bonzini

Imagine a function like this that initializes an object through an external C API:

impl Timer {
    pub fn init<'timer, 'opaque: 'timer, T>(self: Pin<&'timer mut Self>, f: fn(&T), opaque: &'opaque T)  {
        // call to C API
    }
}

When converting this to use PinInit, it is not possible to confirm that the opaque value outlives the timer:

impl Timer {
    pub fn init<T>(f: fn(&T), opaque: *mut T) -> impl PinInit<Self> {
        pin_init_from_closure(move |slot: *mut Timer|
            // call to C API
        }
    }
}

If PinInit received for example an &'a MaybeUninit<T>, it would be possible to do add the constraint to the signature:

impl Timer {
    pub fn init<'timer, 'opaque: 'timer, T: 'opaque>(f: fn(&T), opaque: &'opaque mut T)
                -> impl PinInit<'timer, Self> {
        unsafe {
            pin_init_from_closure(move |slot: &'timer mut MaybeUninit<Timer>| {
                // call to C API
            })
        }
    }
}

MaybeUninit is not suitable (for example it does not support unsized types); but it should be possible to wrap a *mut T and constrain it to a generic lifetime:

pub struct ToInit<'a, T>(*mut T, PhantomData<&'a mut MaybeUninit<T>>);

impl Timer {
    pub fn init<'timer, 'opaque: 'timer, T: 'opaque>(f: fn(&T), opaque: ToInit<'opaque, T>)
                -> impl PinInit<'timer, Self> {
        unsafe {
            pin_init_from_closure(move |slot: ToInit<'timer, Timer>| {
                // call to C API
            })
        }
    }
}

This arises in QEMU, which implements callbacks using (roughly) function pointers instead of traits like Linux.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions