-
Notifications
You must be signed in to change notification settings - Fork 11
__init and __pin_init don't know the lifetime of the created object #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
(I will send this to the Rust-for-Linux mailing list as well once I have something more like a prototype). |
I don't think it's a good idea to add a lifetime to I also think it's possible to do this today without any changes: impl Timer {
pub fn init<'timer, 'opaque: 'timer, T: 'opaque>(
f: fn(&T),
opaque: &'opaque mut T,
) -> impl PinInit<Self> + 'timer {
unsafe {
pin_init_from_closure(|slot| {
// call to C API
})
}
}
} IIUC, you only want to capture the lifetime of fn foo() -> Pin<Box<Timer>> {
let t;
{
let mut opaque = ();
t = Timer::init(|_| (), &mut opaque);
}
Box::pin_init(t).unwrap()
} this is exactly what the example I gave above prevents. However, if you just try to write impl Timer {
pub fn init<'timer, 'opaque: 'timer, T: 'opaque>(
f: fn(&T),
opaque: &'opaque mut T,
) -> impl PinInit<Self> {
unsafe {
pin_init_from_closure(|slot| {
// call to C API
})
}
}
} Then |
The problem is that I don't yet have a fully initialized T, and therefore I have neither a The idea is that I am initializing the outer object and storing a timer into it; the outer object acts as the opaque. What I want to do is allow this: pin_init!(&this in Struct { timer <- Timer::init(Struct::timer_cb, this }) but forbid this: pin_init!(&this in Struct { timer <- Timer::init(Struct::timer_cb, some_other_pointer_to_struct }) |
Ah I see what you want to do... hmm can you share a bit more why you need to access the outer struct in the |
It's just the way the C API works—instead of using container_of in the callback, QEMU has timer initialization take a void* in addition to the timer. It does "waste" a word per timer, but it's how it was designed 20 years ago. :) It does work without needing the complex set of traits that exist in Linux for work items and hrtimers, so it's got some advantages(*). It's not the end of the world if initialization is partly unsafe but more in general, having lifetimes available might make pinned_init more applicable to other usecases than wrapping C APIs. For example, initializing cyclical data structures safely: imagine a I see your point about tracking the lifetime everywhere; even if it can be written as (*) The Linux container_of mechanism also does not extend as well to arrays of timers; say I have a |
I see.
yeah that sounds like a good idea (and like another application for field projection rust-lang/rfcs#3735)
The I'm not 100% opposed to adding the lifetime to Another solution would be to introduce a new type that functions exactly like
Sure!
Yeah both approaches have their (dis)advantages. |
Oh maybe I have understood you incorrectly here, I don't think that we need to add a lifetime to So essentially, we only need the |
Hah you're right - though before edition 2024 you'll need a |
Imagine a function like this that initializes an object through an external C API:
When converting this to use PinInit, it is not possible to confirm that the opaque value outlives the timer:
If PinInit received for example an
&'a MaybeUninit<T>
, it would be possible to do add the constraint to the signature: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:This arises in QEMU, which implements callbacks using (roughly) function pointers instead of traits like Linux.
The text was updated successfully, but these errors were encountered: