I found a way to add field projection as a library without requiring a derive macro like previous methods.
For context, field projection basically lets you do things like this:
struct Foo {
a: i32,
}
fn project(p: Pin<&Foo>) -> Pin<&i32> {
p.a // or something like this
}
What I’ve figured out is that with a trait, Project
, it’s possible to add projection to basically any wrapping struct. Pin
is an obvious one. MaybeUninit
also makes sense.
Here’s the trait:
pub trait Project {
type Base: ?Sized;
type Output<'a, Field: 'a> where Self: 'a;
unsafe fn project<'a, Field>(&'a self, project_field: fn(*const Self::Base) -> *const Field) -> Self::Output<'a, Field>;
}
To use it, you wrap your projections in a macro, proj!
, which is defined as this:
macro_rules! proj {
($input:ident.$field:ident) => {{
unsafe {
<_ as $crate::Project>::project(&$input, |base| unsafe { core::ptr::addr_of!((*base).$field) })
}
}};
(mut $input:ident.$field:ident) => {{
unsafe {
<_ as $crate::ProjectMut>::project_mut(&mut $input, |base| unsafe { core::ptr::addr_of_mut!((*base).$field) })
}
}};
}
Then, you can use this like so:
struct Foo {
a: i32,
}
fn project(p: Pin<&Foo>) -> Pin<&i32> {
proj!(p.a)
}
I’d love to see field projection eventually make it’s way into the language (with better syntax than a macro, of course).
I’ve published a crate for this so people can play around with it: field-project
.