[PATCH 17/20] rust: pin-init: internal: pin_data: complete invariant borrow support

From: Gary Guo

Date: Thu Oct 08 2026 - 08:33:53 EST


Non-covariant types can already be accessed inside projections. As
projections are only generated for `Pin<&mut T>`, they're not accessible
otherwise. Add `with_{field_name}` methods so fields can be accessed using
closures with just `&T`.

Signed-off-by: Gary Guo <gary@xxxxxxxxxxx>
---
rust/pin-init/examples/selfref.rs | 12 ++++++++++++
rust/pin-init/internal/src/pin_data.rs | 19 ++++++++++++++++++-
2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/rust/pin-init/examples/selfref.rs b/rust/pin-init/examples/selfref.rs
index 5e9494dcc17e..5212b9255b75 100644
--- a/rust/pin-init/examples/selfref.rs
+++ b/rust/pin-init/examples/selfref.rs
@@ -6,6 +6,9 @@

#[pin_data]
struct SelfRef {
+ #[uses('_: invariant)]
+ not_cov: Box<dyn Fn(&'str str) -> bool + 'str>,
+
part: &'str str,
str: String,

@@ -20,6 +23,7 @@ fn use_self_ref() {
part: &str[..5],
mut_str: "hello world".to_owned(),
mut_part: &mut mut_str[..5],
+ not_cov: Box::new(move |s| s == str),
}));

// Access via projection.
@@ -41,6 +45,14 @@ fn use_self_ref() {
foo.as_mut().with_project(|proj| {
proj.mut_part.make_ascii_uppercase();
});
+
+ // Access non-covariant type using `with_` accessor.
+ foo.with_not_cov(|not_cov| {
+ not_cov("");
+ });
+
+ // Access non-covariant type using `with_project`.
+ foo.as_mut().with_project(|proj| (proj.not_cov)(proj.str));
}

fn main() {
diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs
index b35d32ff2bb2..b79710ba7059 100644
--- a/rust/pin-init/internal/src/pin_data.rs
+++ b/rust/pin-init/internal/src/pin_data.rs
@@ -1445,7 +1445,24 @@ fn generate_projections(info: &StructInfo) -> TokenStream {
}
))
} else {
- continue;
+ let f_doc = format!("Access the `{ident}` field on a shared reference of `Self`.");
+ let vis = &f.field.vis;
+ let with_ident = format_ident!("with_{ident}");
+
+ let all_lifetimes: Vec<_> = f.captures.iter().map(|b| &b.lifetime).collect();
+ let ty = &f.field.ty;
+
+ accessors.push(quote!(
+ #[doc = #f_doc]
+ #[inline]
+ #vis fn #with_ident<'__this, R>(
+ &'__this self,
+ f: impl for<#(#all_lifetimes,)*> ::core::ops::FnOnce(&'__this #ty) -> R,
+ ) -> R {
+ // SAFETY: `SelfRef` is layout compatible with `#ty`.
+ f(unsafe { ::core::mem::transmute(&self.#ident) })
+ }
+ ))
}
}


--
2.54.0