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 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 77c8ce260403..4b361c510a40 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -1441,7 +1441,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: we have `Erase<..>` which we know is layout compatible with `f.ty`. + f(unsafe { ::core::mem::transmute(&self.#member) }) + } + )) } }