[RFC PATCH 6/6] rust: pin-init: internal: add syn version of `[try_][pin_]init!` macros
From: Benno Lossin
Date: Tue Mar 04 2025 - 18:01:08 EST
Implement the `[try_][pin_]init!` derive macro using syn to simplify
parsing by not going through an additional declarative macro.
This not only simplifies the code by a lot, increasing maintainability
and making it easier to implement new features. But also improves the
user experience by improving the error messages one gets when giving
incorrect inputs to the macro.
For example, placing a `,` after `..Zeroable::zeroed()` is not allowed:
use pin_init::*;
#[derive(Zeroable)]
struct Foo {
a: usize,
b: usize,
}
fn main() {
let _ = init!(Foo {
a: 0,
..Zeroable::zeroed(),
});
}
The declarative macro produces this error:
error: no rules expected `,`
|
11 | let _ = init!(Foo {
| _____________^
12 | | a: 0,
13 | | ..Zeroable::zeroed(),
14 | | });
| |______^ no rules expected this token in macro call
|
note: while trying to match `)`
--> src/macros.rs
|
| @munch_fields($(..Zeroable::zeroed())? $(,)?),
| ^
= note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)
error: no rules expected `,`
|
11 | let _ = init!(Foo {
| _____________^
12 | | a: 0,
13 | | ..Zeroable::zeroed(),
14 | | });
| |______^ no rules expected this token in macro call
|
note: while trying to match `)`
--> src/macros.rs
|
| @munch_fields(..Zeroable::zeroed() $(,)?),
| ^
= note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)
The syn version reduces this error to the much more manageable:
error: unexpected token, expected `}`
|
12 | ..Zeroable::zeroed(),
| ^
This reimplementation is benefiting the most from syn, as can be seen in
this example. It declares a struct with a single generic, but then
supplies two type arguments in the initializer:
use pin_init::*;
struct Foo<T> {
value: T,
}
fn main() {
let _ = init!(Foo::<(), ()> {
value <- (),
});
}
The declarative version emits the following unreadable mess of an error
(shortened for brevity of the commit message):
error: struct literal body without path
|
7 | let _ = init!(Foo::<(), ()> {
| _____________^
8 | | value <- (),
9 | | });
| |______^
|
= note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)
help: you might have forgotten to add the struct literal inside the block
--> src/macros.rs
|
~ ::core::ptr::write($slot, $t { SomeStruct {
|9 $($acc)*
~ } });
|
<...40 lines skipped...>
error[E0061]: this function takes 2 arguments but 3 arguments were supplied
|
7 | let _ = init!(Foo::<(), ()> {
| _____________^
8 | | value <- (),
9 | | });
| |______^ unexpected argument #3
|
note: function defined here
--> $RUST/core/src/ptr/mod.rs
|
| pub const unsafe fn write<T>(dst: *mut T, src: T) {
| ^^^^^
= note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)
This error delightfully reduces to the simple and clear message:
error[E0107]: struct takes 1 generic argument but 2 generic arguments were supplied
|
7 | let _ = init!(Foo::<(), ()> {
| ^^^ ---- help: remove the unnecessary generic argument
| |
| expected 1 generic argument
|
note: struct defined here, with 1 generic parameter: `T`
--> tests/ui/compile-fail/init/wrong_generics2.rs:3:8
|
3 | struct Foo<T> {
| ^^^ -
The syn version is only enabled in the user-space version and disabled
in the kernel until syn becomes available there.
Signed-off-by: Benno Lossin <benno.lossin@xxxxxxxxx>
---
rust/pin-init/internal/src/init.rs | 404 +++++++++++++++++++++++++++++
rust/pin-init/internal/src/lib.rs | 78 ++++++
rust/pin-init/src/__internal.rs | 62 +++++
rust/pin-init/src/lib.rs | 66 +----
4 files changed, 548 insertions(+), 62 deletions(-)
create mode 100644 rust/pin-init/internal/src/init.rs
diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs
new file mode 100644
index 000000000000..4d325cd3c006
--- /dev/null
+++ b/rust/pin-init/internal/src/init.rs
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::{format_ident, quote, quote_spanned};
+use syn::{
+ braced,
+ parse::{Parse, ParseStream},
+ parse_quote,
+ punctuated::Punctuated,
+ spanned::Spanned,
+ token, Error, Expr, ExprCall, ExprPath, Path, Result, Token, Type,
+};
+
+pub(crate) struct InPlaceInitializer {
+ this: Option<This>,
+ path: Path,
+ brace_token: token::Brace,
+ fields: Punctuated<FieldInitializer, Token![,]>,
+ rest: Option<(Token![..], Expr)>,
+ error: Option<(Token![?], Type)>,
+}
+
+struct This {
+ _and_token: Token![&],
+ ident: Ident,
+ _in_token: Token![in],
+}
+
+enum FieldInitializer {
+ Value {
+ ident: Ident,
+ value: Option<(Token![:], Expr)>,
+ },
+ Init {
+ ident: Ident,
+ _larrow: Token![<-],
+ value: Expr,
+ },
+}
+
+impl FieldInitializer {
+ fn ident(&self) -> &Ident {
+ match self {
+ FieldInitializer::Value { ident, .. } | FieldInitializer::Init { ident, .. } => ident,
+ }
+ }
+}
+
+enum InitKind {
+ Normal,
+ Zeroing,
+}
+
+pub(crate) fn init(
+ InPlaceInitializer {
+ this,
+ path,
+ fields,
+ rest,
+ mut error,
+ brace_token,
+ ..
+ }: InPlaceInitializer,
+ default_error: bool,
+ pin: bool,
+) -> Result<TokenStream> {
+ if default_error {
+ error.get_or_insert((
+ Default::default(),
+ parse_quote!(::core::convert::Infallible),
+ ));
+ }
+ let Some((_, error)) = error else {
+ return Err(Error::new(
+ brace_token.span.close(),
+ "expected `? <type>` after `}`",
+ ));
+ };
+ let use_data = pin;
+ let (has_data_trait, data_trait, get_data, from_closure) = match pin {
+ true => (
+ format_ident!("HasPinData"),
+ format_ident!("PinData"),
+ format_ident!("__pin_data"),
+ format_ident!("pin_init_from_closure"),
+ ),
+ false => (
+ format_ident!("HasInitData"),
+ format_ident!("InitData"),
+ format_ident!("__init_data"),
+ format_ident!("init_from_closure"),
+ ),
+ };
+
+ let init_kind = get_init_kind(rest)?;
+ let zeroable_check = match init_kind {
+ InitKind::Normal => quote! {},
+ InitKind::Zeroing => quote! {
+ // The user specified `..Zeroable::zeroed()` at the end of the list of fields.
+ // Therefore we check if the struct implements `Zeroable` and then zero the memory.
+ // This allows us to also remove the check that all fields are present (since we
+ // already set the memory to zero and that is a valid bit pattern).
+ fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
+ where T: ::pin_init::Zeroable
+ {}
+ // Ensure that the struct is indeed `Zeroable`.
+ assert_zeroable(slot);
+ // SAFETY: The type implements `Zeroable` by the check above.
+ unsafe { ::core::ptr::write_bytes(slot, 0, 1) };
+ },
+ };
+
+ let this = match this {
+ None => quote!(),
+ Some(This { ident, .. }) => quote! {
+ // Create the `this` so it can be referenced by the user inside of the
+ // expressions creating the individual fields.
+ let #ident = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };
+ },
+ };
+ let data = format_ident!("data", span = Span::mixed_site());
+ let init_fields = init_fields(&fields, use_data, &data);
+ let field_check = make_field_check(&fields, init_kind, &path);
+ Ok(quote! {{
+ // We do not want to allow arbitrary returns, so we declare this type as the `Ok` return
+ // type and shadow it later when we insert the arbitrary user code. That way there will be
+ // no possibility of returning without `unsafe`.
+ struct __InitOk;
+ // Get the data about fields from the supplied type.
+ let #data = unsafe {
+ use ::pin_init::__internal::#has_data_trait;
+ // Can't use `<#path as #has_data_trait>::#get_data`, since the user is able to omit
+ // generics (which need to be present with that syntax).
+ #path::#get_data()
+ };
+ // Ensure that `#data` really is of type `#data` and help with type inference:
+ let init = ::pin_init::__internal::#data_trait::make_closure::<_, __InitOk, #error>(
+ #data,
+ move |slot| {
+ {
+ // Shadow the structure so it cannot be used to return early.
+ struct __InitOk;
+
+ #zeroable_check
+
+ #this
+
+ #init_fields
+
+ #field_check
+ }
+ Ok(__InitOk)
+ }
+ );
+ let init = move |slot| -> ::core::result::Result<(), #error> {
+ init(slot).map(|__InitOk| ())
+ };
+ let init = unsafe { ::pin_init::#from_closure::<_, #error>(init) };
+ init
+ }})
+}
+
+fn get_init_kind(rest: Option<(Token![..], Expr)>) -> Result<InitKind> {
+ let Some((dotdot, expr)) = rest else {
+ return Ok(InitKind::Normal);
+ };
+ let error = Error::new_spanned(
+ quote!(#dotdot #expr),
+ "Expected one of the following:\n- Nothing.\n- `..Zeroable::zeroed()`.",
+ );
+ let Expr::Call(ExprCall {
+ func, args, attrs, ..
+ }) = expr
+ else {
+ return Err(error);
+ };
+ if !args.is_empty() || !attrs.is_empty() {
+ return Err(error);
+ }
+ match *func {
+ Expr::Path(ExprPath {
+ attrs,
+ qself: None,
+ path:
+ Path {
+ leading_colon: None,
+ segments,
+ },
+ }) if attrs.is_empty()
+ && segments.len() == 2
+ && segments[0].ident == "Zeroable"
+ && segments[0].arguments.is_none()
+ && segments[1].ident == "zeroed"
+ && segments[1].arguments.is_none() =>
+ {
+ Ok(InitKind::Zeroing)
+ }
+ _ => Err(error),
+ }
+}
+
+/// Generate the code that initializes the fields of the struct using the initializers in `field`.
+fn init_fields(
+ fields: &Punctuated<FieldInitializer, Token![,]>,
+ use_data: bool,
+ data: &Ident,
+) -> TokenStream {
+ let mut guards = vec![];
+ let mut res = TokenStream::new();
+ for field in fields {
+ let ident = field.ident();
+ let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
+ guards.push(guard.clone());
+ let init = match field {
+ FieldInitializer::Value { ident, value } => {
+ let mut value_ident = ident.clone();
+ let value = value.as_ref().map(|value| &value.1).map(|value| {
+ // Setting the span of `value_ident` to `value`'s span improves error messages
+ // when the type of `value` is wrong.
+ value_ident.set_span(value.span());
+ quote!(let #value_ident = #value;)
+ });
+ // Again span for better diagnostics
+ let write = quote_spanned! {ident.span()=>
+ ::core::ptr::write
+ };
+ quote! {
+ {
+ #value
+ // Initialize the field.
+ //
+ // SAFETY: The memory at `slot` is uninitialized.
+ unsafe { #write(::core::ptr::addr_of_mut!((*slot).#ident), #value_ident) };
+ }
+ }
+ }
+ FieldInitializer::Init { ident, value, .. } => {
+ if use_data {
+ quote! {
+ let init = #value;
+ // Call the initializer.
+ //
+ // SAFETY: `slot` is valid, because we are inside of an initializer closure,
+ // we return when an error/panic occurs.
+ // We also use the `#data` to require the correct trait (`Init` or `PinInit`)
+ // for `#ident`.
+ unsafe { #data.#ident(::core::ptr::addr_of_mut!((*slot).#ident), init)? };
+ }
+ } else {
+ quote! {
+ let init = #value;
+ // Call the initializer.
+ //
+ // SAFETY: `slot` is valid, because we are inside of an initializer closure,
+ // we return when an error/panic occurs.
+ unsafe {
+ ::pin_init::Init::__init(
+ init,
+ ::core::ptr::addr_of_mut!((*slot).#ident),
+ )?
+ };
+ }
+ }
+ }
+ };
+ res.extend(init);
+ res.extend(quote! {
+ // Create the drop guard:
+ //
+ // We rely on macro hygiene to make it impossible for users to access this local
+ // variable.
+ // SAFETY: We forget the guard later when initialization has succeeded.
+ let #guard = unsafe {
+ ::pin_init::__internal::DropGuard::new(
+ ::core::ptr::addr_of_mut!((*slot).#ident)
+ )
+ };
+ });
+ }
+ quote! {
+ #res
+ // If execution reaches this point, all fields have been initialized. Therefore we can now
+ // dismiss the guards by forgetting them.
+ #(::core::mem::forget(#guards);)*
+ }
+}
+
+/// Generate the check for ensuring that every field has been initialized.
+fn make_field_check(
+ fields: &Punctuated<FieldInitializer, Token![,]>,
+ init_kind: InitKind,
+ path: &Path,
+) -> TokenStream {
+ let fields = fields.iter().map(|f| f.ident());
+ match init_kind {
+ InitKind::Normal => quote! {
+ // We use unreachable code to ensure that all fields have been mentioned exactly once,
+ // this struct initializer will still be type-checked and complain with a very natural
+ // error message if a field is forgotten/mentioned more than once.
+ #[allow(unreachable_code, clippy::diverging_sub_expression)]
+ // SAFETY: this code is never executed.
+ let _ = || unsafe {
+ ::core::ptr::write(slot, #path {
+ #(
+ #fields: ::core::panic!(),
+ )*
+ })
+ };
+ },
+ InitKind::Zeroing => quote! {
+ // We use unreachable code to ensure that all fields have been mentioned at most once.
+ // Since the user specified `..Zeroable::zeroed()` at the end, all missing fields will
+ // be zeroed. This struct initializer will still be type-checked and complain with a
+ // very natural error message if a field is mentioned more than once, or doesn't exist.
+ #[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)]
+ // SAFETY: this code is never executed.
+ let _ = || unsafe {
+ let mut zeroed = ::core::mem::zeroed();
+ ::core::ptr::write(slot, zeroed);
+ zeroed = ::core::mem::zeroed();
+ ::core::ptr::write(slot, #path {
+ #(
+ #fields: ::core::panic!(),
+ )*
+ ..zeroed
+ })
+ };
+ },
+ }
+}
+
+impl Parse for InPlaceInitializer {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let content;
+ Ok(Self {
+ this: input.peek(Token![&]).then(|| input.parse()).transpose()?,
+ path: input.parse()?,
+ brace_token: braced!(content in input),
+ fields: {
+ let mut fields = Punctuated::new();
+ loop {
+ let lookahead = content.lookahead1();
+ if content.is_empty() || lookahead.peek(Token![..]) {
+ break;
+ } else if lookahead.peek(syn::Ident) {
+ fields.push_value(content.parse()?);
+ let lookahead = content.lookahead1();
+ if lookahead.peek(Token![,]) {
+ fields.push_punct(content.parse()?);
+ } else if content.is_empty() {
+ break;
+ } else {
+ return Err(lookahead.error());
+ }
+ } else {
+ return Err(lookahead.error());
+ }
+ }
+ fields
+ },
+ rest: content
+ .peek(Token![..])
+ .then(|| -> Result<_> { Ok((content.parse()?, content.parse()?)) })
+ .transpose()?,
+ error: input
+ .peek(Token![?])
+ .then(|| -> Result<_> { Ok((input.parse()?, input.parse()?)) })
+ .transpose()?,
+ })
+ }
+}
+
+impl Parse for This {
+ fn parse(input: ParseStream) -> Result<Self> {
+ Ok(Self {
+ _and_token: input.parse()?,
+ ident: input.parse()?,
+ _in_token: input.parse()?,
+ })
+ }
+}
+
+impl Parse for FieldInitializer {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let ident = input.parse()?;
+ let lookahead = input.lookahead1();
+ Ok(if lookahead.peek(Token![<-]) {
+ Self::Init {
+ ident,
+ _larrow: input.parse()?,
+ value: input.parse()?,
+ }
+ } else if lookahead.peek(Token![:]) {
+ Self::Value {
+ ident,
+ value: Some((input.parse()?, input.parse()?)),
+ }
+ } else if lookahead.peek(Token![,]) || input.is_empty() {
+ Self::Value { ident, value: None }
+ } else {
+ return Err(lookahead.error());
+ })
+ }
+}
diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index 9ef6178e410f..bf524161022a 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -32,6 +32,8 @@
#[cfg(kernel)]
mod zeroable;
+#[cfg(not(kernel))]
+mod init;
#[cfg(not(kernel))]
#[path = "syn_pin_data.rs"]
mod pin_data;
@@ -56,3 +58,79 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
pub fn derive_zeroable(input: TokenStream) -> TokenStream {
zeroable::derive(input.into()).into()
}
+
+#[cfg(kernel)]
+#[proc_macro]
+pub fn pin_init(input: TokenStream) -> TokenStream {
+ quote!(::pin_init::__internal_pin_init!(#input))
+}
+
+#[cfg(not(kernel))]
+#[proc_macro]
+pub fn pin_init(input: TokenStream) -> TokenStream {
+ use syn::parse_macro_input;
+ init::init(
+ parse_macro_input!(input as init::InPlaceInitializer),
+ true,
+ true,
+ )
+ .unwrap_or_else(|e| e.into_compile_error())
+ .into()
+}
+
+#[cfg(kernel)]
+#[proc_macro]
+pub fn init(input: TokenStream) -> TokenStream {
+ quote!(::pin_init::__internal_init!(#input))
+}
+
+#[cfg(not(kernel))]
+#[proc_macro]
+pub fn init(input: TokenStream) -> TokenStream {
+ use syn::parse_macro_input;
+ init::init(
+ parse_macro_input!(input as init::InPlaceInitializer),
+ true,
+ false,
+ )
+ .unwrap_or_else(|e| e.into_compile_error())
+ .into()
+}
+
+#[cfg(kernel)]
+#[proc_macro]
+pub fn try_pin_init(input: TokenStream) -> TokenStream {
+ quote!(::pin_init::__internal_try_pin_init!(#input))
+}
+
+#[cfg(not(kernel))]
+#[proc_macro]
+pub fn try_pin_init(input: TokenStream) -> TokenStream {
+ use syn::parse_macro_input;
+ init::init(
+ parse_macro_input!(input as init::InPlaceInitializer),
+ false,
+ true,
+ )
+ .unwrap_or_else(|e| e.into_compile_error())
+ .into()
+}
+
+#[cfg(kernel)]
+#[proc_macro]
+pub fn try_init(input: TokenStream) -> TokenStream {
+ quote!(::pin_init::__internal_try_init!(#input))
+}
+
+#[cfg(not(kernel))]
+#[proc_macro]
+pub fn try_init(input: TokenStream) -> TokenStream {
+ use syn::parse_macro_input;
+ init::init(
+ parse_macro_input!(input as init::InPlaceInitializer),
+ false,
+ false,
+ )
+ .unwrap_or_else(|e| e.into_compile_error())
+ .into()
+}
diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs
index 6b258502a9d7..741af6f2fdf6 100644
--- a/rust/pin-init/src/__internal.rs
+++ b/rust/pin-init/src/__internal.rs
@@ -287,3 +287,65 @@ unsafe fn __pinned_init(self, _slot: *mut T) -> Result<(), ()> {
Err(())
}
}
+
+#[cfg(kernel)]
+#[macro_export]
+macro_rules! __internal_init {
+ ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
+ $($fields:tt)*
+ }) => {
+ $crate::try_init!($(&$this in)? $t $(::<$($generics),*>)? {
+ $($fields)*
+ }? ::core::convert::Infallible)
+ };
+}
+
+#[cfg(kernel)]
+#[macro_export]
+macro_rules! __internal_pin_init {
+ ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
+ $($fields:tt)*
+ }) => {
+ $crate::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
+ $($fields)*
+ }? ::core::convert::Infallible)
+ };
+}
+
+#[cfg(kernel)]
+#[macro_export]
+macro_rules! __internal_try_init {
+ ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
+ $($fields:tt)*
+ }? $err:ty) => {
+ $crate::__init_internal!(
+ @this($($this)?),
+ @typ($t $(::<$($generics),*>)?),
+ @fields($($fields)*),
+ @error($err),
+ @data(InitData, /*no use_data*/),
+ @has_data(HasInitData, __init_data),
+ @construct_closure(init_from_closure),
+ @munch_fields($($fields)*),
+ )
+ };
+}
+
+#[cfg(kernel)]
+#[macro_export]
+macro_rules! __internal_try_pin_init {
+ ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
+ $($fields:tt)*
+ }? $err:ty) => {
+ $crate::__init_internal!(
+ @this($($this)?),
+ @typ($t $(::<$($generics),*>)? ),
+ @fields($($fields)*),
+ @error($err),
+ @data(PinData, use_data),
+ @has_data(HasPinData, __pin_data),
+ @construct_closure(pin_init_from_closure),
+ @munch_fields($($fields)*),
+ )
+ };
+}
diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs
index 356fc959bcb9..a5d86aae2e59 100644
--- a/rust/pin-init/src/lib.rs
+++ b/rust/pin-init/src/lib.rs
@@ -717,18 +717,7 @@ macro_rules! stack_try_pin_init {
/// ```
///
/// [`NonNull<Self>`]: core::ptr::NonNull
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! pin_init {
- ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
- $($fields:tt)*
- }) => {
- $crate::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? {
- $($fields)*
- }? ::core::convert::Infallible)
- };
-}
+pub use pin_init_internal::pin_init;
/// Construct an in-place, fallible pinned initializer for `struct`s.
///
@@ -768,25 +757,7 @@ macro_rules! pin_init {
/// }
/// # let _ = Box::pin_init(BigBuf::new());
/// ```
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! try_pin_init {
- ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
- $($fields:tt)*
- }? $err:ty) => {
- $crate::__init_internal!(
- @this($($this)?),
- @typ($t $(::<$($generics),*>)? ),
- @fields($($fields)*),
- @error($err),
- @data(PinData, use_data),
- @has_data(HasPinData, __pin_data),
- @construct_closure(pin_init_from_closure),
- @munch_fields($($fields)*),
- )
- }
-}
+pub use pin_init_internal::try_pin_init;
/// Construct an in-place initializer for `struct`s.
///
@@ -824,18 +795,7 @@ macro_rules! try_pin_init {
/// }
/// # let _ = Box::init(BigBuf::new());
/// ```
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! init {
- ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
- $($fields:tt)*
- }) => {
- $crate::try_init!($(&$this in)? $t $(::<$($generics),*>)? {
- $($fields)*
- }? ::core::convert::Infallible)
- }
-}
+pub use pin_init_internal::init;
/// Construct an in-place fallible initializer for `struct`s.
///
@@ -873,25 +833,7 @@ macro_rules! init {
/// }
/// # let _ = Box::init(BigBuf::new());
/// ```
-// For a detailed example of how this macro works, see the module documentation of the hidden
-// module `macros` inside of `macros.rs`.
-#[macro_export]
-macro_rules! try_init {
- ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
- $($fields:tt)*
- }? $err:ty) => {
- $crate::__init_internal!(
- @this($($this)?),
- @typ($t $(::<$($generics),*>)?),
- @fields($($fields)*),
- @error($err),
- @data(InitData, /*no use_data*/),
- @has_data(HasInitData, __init_data),
- @construct_closure(init_from_closure),
- @munch_fields($($fields)*),
- )
- }
-}
+pub use pin_init_internal::try_init;
/// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is
/// structurally pinned.
--
2.48.1