-
-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[lib] In-place initialization infrastructure #142518
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| //! In-place initialization. | ||
| //! | ||
| //! This module describes the interface through which types supporting in-place initialization | ||
| //! can perform initialization with minimal or zero additional allocations or moves. | ||
|
|
||
| use crate::ptr::Pointee; | ||
|
|
||
| /// In-place Initializer for `T`. | ||
| /// | ||
| /// An instance of `Init<T>` carries all the information necessary to initialize a `T` in a | ||
| /// well-defined memory location, criteria of which is prescribed in the Safety section. | ||
| /// | ||
| /// # Fallibility | ||
| /// | ||
| /// The initialization might fail and return an error of type [`Self::Error`] instead. | ||
| /// In that case, the memory provided to [`Self::init`] shall be repurposed in any way, | ||
| /// even though it might have been written to by this initializer. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ## Initializing unsized types | ||
| /// | ||
| /// To initialize an unsized type, first query the required layout for `T` using [`Self::layout`]. | ||
| /// Then provide a pointer to an allocation of at least the specified alignment and size. | ||
| /// | ||
| /// If initialization was successful, then [`Self::init`] returns the metadata that combined with | ||
| /// the pointer to the given to [`Self::init`] yields a valid pointer to `T`. | ||
| /// | ||
| /// ``` ignore (illustrative) | ||
| /// use std::alloc::alloc; | ||
| /// fn init_boxed<T: ?Sized + Pointee, I: Init<T>>(init: I) -> Result<Box<T>, I::Error> { | ||
| /// let layout = init.layout(); | ||
| /// let memory = alloc(layout).cast::<()>(); | ||
| /// let meta = init.init(memory)?; | ||
| /// Box::from_raw(from_raw_parts_mut(memory, meta)) | ||
| /// } | ||
| /// ``` | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// Implementers must ensure that if [`self.init(slot)`] returns `Ok(metadata)`, | ||
| /// then [`core::ptr::from_raw_parts_mut(slot, metadata)`] must reference a valid | ||
| /// value owned by the caller. | ||
| /// Furthermore, the layout returned by using | ||
| /// [`core::intrinsics::size_of_val`] and [`core::intrinsics::align_of_val`] on this pointer | ||
| /// must match what [`Self::layout()`] returns exactly. | ||
| /// | ||
| /// If `T` is sized, or in other words `T: Sized`, [`Init::layout`] in this case *must* | ||
| /// return the same layout as [`Layout::new::<T>()`] would. | ||
| /// | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Additionally, we should require that if
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Applied |
||
| /// Implementers must ensure that the implementation of `init()` does not rely | ||
| /// on the value being pinned. | ||
| /// | ||
| /// [`core::ptr::from_raw_parts_mut(slot, metadata)`]: core::ptr::from_raw_parts_mut | ||
| /// [`Self::layout()`]: Init::layout | ||
| /// [`self.init(slot)`]: Init::init | ||
| /// [`Layout::new::<T>()`]: core::alloc::Layout::new | ||
| #[unstable(feature = "in_place_initialization", issue = "999999")] | ||
| #[lang = "init_trait"] | ||
| pub unsafe trait Init<T: ?Sized + Pointee> { | ||
| /// Error type upon initialization failure during the actual | ||
| /// in-place initialization procedure. | ||
| #[lang = "init_error"] | ||
| type Error; | ||
|
|
||
| /// The layout needed by this initializer. | ||
| /// This method must return a layout that precisely matches | ||
| /// with `T`. | ||
| /// Namely the size and the alignment must be equal. | ||
| #[lang = "init_layout"] | ||
| fn layout(&self) -> crate::alloc::Layout; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm assuming it's not allowed to have Would it be better to only expose the layout/metadata in a single place, like replacing Then the trait user would do
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that the metadata should be able to depend on what
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Then
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. okay fair. But how do you get the alignment & size from the metadata? I didn't understand what you wrote above:
You don't have
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calling
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That case is wrong too, but I meant what I said. The layout must be an exact match - it's not okay for
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Oh, I didn't realize that there is no method to get a unsafe fn layout_from_meta<T: ptr::Pointee + ?Sized>(meta: T::Metadata) -> Layout {
unsafe {
// wait, if `for_value_raw` only care about ptr metadata, why doesn't it just
// accept a `T::Metadata` parameter?
Layout::for_value_raw(ptr::from_raw_parts::<T>(ptr::null::<()>(), meta))
}
}
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have updated the comment to make it very specific.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 for returning
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think it is quite fair to return |
||
|
|
||
| /// Writes a valid value of type `T` to `slot` or fails. | ||
| /// | ||
| /// If this call returns [`Ok`], then `slot` is guaranteed to contain a valid | ||
| /// value of type `T`. | ||
| /// Otherwise, in case the result is an [`Err`], the implementation must guarantee | ||
| /// that the `slot` may be repurposed for future reuse. | ||
| /// | ||
| /// If `T` is unsized, then `slot` may be combined with | ||
| /// the metadata to obtain a valid pointer to the value. | ||
| /// | ||
| /// Note that `slot` should be thought of as a `*mut T`. A unit type is used | ||
| /// so that the pointer is thin even if `T` is unsized. | ||
| /// | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're missing the part that if it returns
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Applied with additional comments
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see it?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I forgot to add the hunk to the change set. I would like to make it more explicit. Maybe it will become a future OpSem question. Should we make the I think we can defer it for further clarification. We can come back to update the documentation at any time before calling for stabilisation. |
||
| /// # Safety | ||
| /// | ||
| /// The caller must provide a pointer that references a location that `init` | ||
| /// may write to, and the location must have at least the size and alignment | ||
| /// specified by [`Init::layout`]. | ||
| #[lang = "init_init"] | ||
| unsafe fn init(self, slot: *mut ()) -> Result<T::Metadata, Self::Error>; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.