field_project/
lib.rs

1#![feature(generic_associated_types)]
2#![deny(unsafe_op_in_unsafe_fn)]
3
4mod pin;
5mod maybe_uninit;
6
7pub trait Project {
8    type Base: ?Sized;
9    type Output<'a, Field: 'a> where Self: 'a;
10    
11    unsafe fn project<'a, Field>(&'a self, project_field: fn(*const Self::Base) -> *const Field) -> Self::Output<'a, Field>;
12}
13
14pub trait ProjectMut: Project {
15    type OutputMut<'a, Field: 'a> where Self: 'a;
16    
17    unsafe fn project_mut<'a, Field>(&'a mut self, project_field: fn(*mut Self::Base) -> *mut Field) -> Self::OutputMut<'a, Field>;
18}
19
20impl<T> Project for &'_ T where T: Project {
21    type Base = T::Base;
22    type Output<'a, Field: 'a> where Self: 'a = T::Output<'a, Field>;
23
24    unsafe fn project<'a, Field>(&'a self, project_field: fn(*const Self::Base) -> *const Field) -> Self::Output<'a, Field> {
25        unsafe {
26            T::project(*self, project_field)
27        }
28    }
29}
30
31impl<T> Project for &'_ mut T where T: Project {
32    type Base = T::Base;
33    type Output<'a, Field: 'a> where Self: 'a = T::Output<'a, Field>;
34
35    unsafe fn project<'a, Field>(&'a self, project_field: fn(*const Self::Base) -> *const Field) -> Self::Output<'a, Field> {
36        unsafe {
37            T::project(*self, project_field)
38        }
39    }
40}
41
42impl<T> ProjectMut for &'_ mut T where T: ProjectMut {
43    type OutputMut<'a, Field: 'a> where Self: 'a = T::OutputMut<'a, Field>;
44
45    unsafe fn project_mut<'a, Field>(&'a mut self, project_field: fn(*mut Self::Base) -> *mut Field) -> Self::OutputMut<'a, Field> {
46        unsafe {
47            T::project_mut(*self, project_field)
48        }
49    }
50}
51
52impl<T> Project for Box<T> where T: Project {
53    type Base = T::Base;
54    type Output<'a, Field: 'a> where Self: 'a = T::Output<'a, Field>;
55
56    unsafe fn project<'a, Field>(&'a self, project_field: fn(*const Self::Base) -> *const Field) -> Self::Output<'a, Field> {
57        unsafe {
58            T::project(&**self, project_field)
59        }
60    }
61}
62
63/// Use [`proj!`] to project a wrapper struct, like [`std::pin::Pin`], onto a field of the wrapped type.
64/// 
65/// # Example
66/// ```rust
67/// # use field_project::proj;
68/// struct Foo {
69///     a: i32,
70///     b: &'static str,
71/// }
72/// 
73/// let foo = Box::pin(Foo { a: 42, b: "hello, world" });
74///
75/// let a: Pin<_> = proj!(foo.a);
76/// let b = proj!(foo.b);
77/// ```
78#[macro_export]
79macro_rules! proj {
80    ($input:ident.$field:ident) => {{
81        unsafe {
82            <_ as $crate::Project>::project(&$input, |base| unsafe { core::ptr::addr_of!((*base).$field) })
83        }
84    }};
85    (mut $input:ident.$field:ident) => {{
86        unsafe {
87            <_ as $crate::ProjectMut>::project_mut(&mut $input, |base| unsafe { core::ptr::addr_of_mut!((*base).$field) })
88        }
89    }};
90}