[PATCH 2/8] rust: pin-init: internal: pin_data: add struct to record field info

From: Gary Guo

Date: Tue May 12 2026 - 08:10:22 EST


From: Mohamad Alsadhan <mo@xxxxxxx>

Introduce `FieldInfo` struct to encapsulate field and other relevant data,
instead of carrying a pair of `(pinned, field)` in all places. This allows
us to add more information to the struct in the future.

Signed-off-by: Mohamad Alsadhan <mo@xxxxxxx>
Co-developed-by: Gary Guo <gary@xxxxxxxxxxx>
Signed-off-by: Gary Guo <gary@xxxxxxxxxxx>
---
rust/pin-init/internal/src/pin_data.rs | 53 ++++++++++++++++++++--------------
1 file changed, 32 insertions(+), 21 deletions(-)

diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs
index 1a7098a4c6e0..0199d0143308 100644
--- a/rust/pin-init/internal/src/pin_data.rs
+++ b/rust/pin-init/internal/src/pin_data.rs
@@ -35,6 +35,11 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
}
}

+struct FieldInfo<'a> {
+ field: &'a Field,
+ pinned: bool,
+}
+
pub(crate) fn pin_data(
args: Args,
input: Item,
@@ -73,24 +78,30 @@ pub(crate) fn pin_data(
replacer.visit_generics_mut(&mut struct_.generics);
replacer.visit_fields_mut(&mut struct_.fields);

- let fields: Vec<(bool, &Field)> = struct_
+ let fields: Vec<FieldInfo<'_>> = struct_
.fields
.iter_mut()
.map(|field| {
let len = field.attrs.len();
field.attrs.retain(|a| !a.path().is_ident("pin"));
- (len != field.attrs.len(), &*field)
+ let pinned = len != field.attrs.len();
+
+ FieldInfo {
+ field: &*field,
+ pinned,
+ }
})
.collect();

- for (pinned, field) in &fields {
- if !pinned && is_phantom_pinned(&field.ty) {
+ for field in &fields {
+ let ident = field.field.ident.as_ref().unwrap();
+
+ if !field.pinned && is_phantom_pinned(&field.field.ty) {
dcx.warn(
- field,
+ field.field,
format!(
- "The field `{}` of type `PhantomPinned` only has an effect \
+ "The field `{ident}` of type `PhantomPinned` only has an effect \
if it has the `#[pin]` attribute",
- field.ident.as_ref().unwrap(),
),
);
}
@@ -143,7 +154,7 @@ fn is_phantom_pinned(ty: &Type) -> bool {
fn generate_unpin_impl(
ident: &Ident,
generics: &Generics,
- fields: &[(bool, &Field)],
+ fields: &[FieldInfo<'_>],
) -> TokenStream {
let (_, ty_generics, _) = generics.split_for_impl();
let mut generics_with_pin_lt = generics.clone();
@@ -160,7 +171,7 @@ fn generate_unpin_impl(
else {
unreachable!()
};
- let pinned_fields = fields.iter().filter_map(|(b, f)| b.then_some(f));
+ let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| f.field);
quote! {
// This struct will be used for the unpin analysis. It is needed, because only structurally
// pinned fields are relevant whether the struct should implement `Unpin`.
@@ -238,7 +249,7 @@ fn generate_projections(
vis: &Visibility,
ident: &Ident,
generics: &Generics,
- fields: &[(bool, &Field)],
+ fields: &[FieldInfo<'_>],
) -> TokenStream {
let (impl_generics, ty_generics, _) = generics.split_for_impl();
let mut generics_with_pin_lt = generics.clone();
@@ -249,21 +260,21 @@ fn generate_projections(

let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields
.iter()
- .map(|(pinned, field)| {
+ .map(|field| {
let Field {
vis,
ident,
ty,
attrs,
..
- } = field;
+ } = &field.field;

let mut no_doc_attrs = attrs.clone();
no_doc_attrs.retain(|a| !a.path().is_ident("doc"));
let ident = ident
.as_ref()
.expect("only structs with named fields are supported");
- if *pinned {
+ if field.pinned {
(
quote!(
#(#attrs)*
@@ -291,12 +302,12 @@ fn generate_projections(
.collect();
let structurally_pinned_fields_docs = fields
.iter()
- .filter_map(|(pinned, field)| pinned.then_some(field))
- .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap()));
+ .filter(|f| f.pinned)
+ .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
let not_structurally_pinned_fields_docs = fields
.iter()
- .filter_map(|(pinned, field)| (!pinned).then_some(field))
- .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap()));
+ .filter(|f| !f.pinned)
+ .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
let docs = format!(" Pin-projections of [`{ident}`]");
quote! {
#[doc = #docs]
@@ -338,7 +349,7 @@ fn generate_the_pin_data(
vis: &Visibility,
struct_name: &Ident,
generics: &Generics,
- fields: &[(bool, &Field)],
+ fields: &[FieldInfo<'_>],
) -> TokenStream {
let (impl_generics, ty_generics, whr) = generics.split_for_impl();

@@ -349,20 +360,20 @@ fn generate_the_pin_data(
// The functions are `unsafe` to prevent accidentally calling them.
let field_accessors = fields
.iter()
- .map(|(pinned, field)| {
+ .map(|f| {
let Field {
vis,
ident,
ty,
attrs,
..
- } = field;
+ } = f.field;

let field_name = ident
.as_ref()
.expect("only structs with named fields are supported");
let project_ident = format_ident!("__project_{field_name}");
- let (init_ty, init_fn, project_ty, project_body, pin_safety) = if *pinned {
+ let (init_ty, init_fn, project_ty, project_body, pin_safety) = if f.pinned {
(
quote!(PinInit),
quote!(__pinned_init),

--
2.51.2