[PATCH v2 13/20] rust: pin-init: internal: pin_data: parse explicit `#[borrowed]` annotation

From: Gary Guo

Date: Thu Oct 08 2026 - 15:36:04 EST


`#[borrowed]` attribute explicitly marks a field as potentially being
borrowed by other fields.

Acked-by: Benno Lossin <lossin@xxxxxxxxxx>
Signed-off-by: Gary Guo <gary@xxxxxxxxxxx>
---
rust/pin-init/internal/src/pin_data.rs | 36 ++++++++++++++++++++++++++++++----
1 file changed, 32 insertions(+), 4 deletions(-)

diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs
index 6af1f7ae0a08..c5b664349caf 100644
--- a/rust/pin-init/internal/src/pin_data.rs
+++ b/rust/pin-init/internal/src/pin_data.rs
@@ -11,8 +11,8 @@
spanned::Spanned,
visit::Visit,
visit_mut::VisitMut,
- Field, Fields, GenericParam, Generics, Ident, Index, Item, ItemStruct, Lifetime, LifetimeParam,
- Member, PathSegment, Token, Type, TypePath,
+ Attribute, Field, Fields, GenericParam, Generics, Ident, Index, Item, ItemStruct, Lifetime,
+ LifetimeParam, Member, Meta, PathSegment, Token, Type, TypePath,
};

use crate::{
@@ -55,11 +55,25 @@ fn to_tokens(&self, tokens: &mut TokenStream) {
/// Description of how a field is borrowed.
#[derive(Clone, Copy, Default, PartialEq, Eq)]
enum BorrowedKind {
- /// Implicitly inferreed.
+ /// `#[borrowed]`, or implicitly inferreed.
#[default]
Shared,
}

+impl BorrowedKind {
+ fn parse(dcx: &mut DiagCtxt, attrs: &mut Vec<Attribute>) -> Option<Self> {
+ let attr = attrs.extract_single_attr(dcx, "borrowed")?;
+
+ Some(if let Meta::Path(_) = attr.meta {
+ BorrowedKind::Shared
+ } else {
+ // Swallow the error and recover by inferring shared.
+ dcx.error(attr.path(), "unexpected `#[borrowed]` attribute");
+ BorrowedKind::Shared
+ })
+ }
+}
+
/// Information about a borrowed field.
struct BorrowedInfo {
kind: BorrowedKind,
@@ -305,11 +319,25 @@ fn expand(
})
.visit_type(&field.ty);

+ let borrowed = BorrowedKind::parse(dcx, &mut field.attrs).and_then(|kind| {
+ let lifetime = Lifetime::from_ident(&member.as_ident());
+
+ if bound_lifetimes.contains(&lifetime) {
+ dcx.error(
+ &lifetime,
+ format!("`{lifetime}` appear in generics and would conflict with field lifetime"),
+ );
+ return None;
+ }
+
+ Some(BorrowedInfo { kind, lifetime })
+ });
+
FieldInfo {
field,
member,
pinned,
- borrowed: None,
+ borrowed,
captures,
generic_lt_captures,
generic_ty_captures,

--
2.54.0