field_offset/
lib.rs

1#![no_std]
2#![cfg_attr(fieldoffset_assert_in_const_fn, feature(const_panic))]
3// Explicit lifetimes are clearer when we are working with raw pointers,
4// as the compiler will not warn us if we specify lifetime constraints
5// which are too lax.
6#![allow(clippy::needless_lifetimes)]
7
8#[cfg(all(test, fieldoffset_has_alloc))]
9extern crate alloc;
10
11use core::fmt;
12use core::marker::PhantomData;
13use core::mem;
14use core::ops::Add;
15use core::pin::Pin;
16
17#[doc(hidden)]
18pub extern crate memoffset as __memoffset; // `pub` for macro availability
19
20/// Represents a pointer to a field of type `U` within the type `T`
21///
22/// The `PinFlag` parameter can be set to `AllowPin` to enable the projection
23/// from Pin<&T> to Pin<&U>
24#[repr(transparent)]
25pub struct FieldOffset<T, U, PinFlag = NotPinned>(
26    /// Offset in bytes of the field within the struct
27    usize,
28    /// A pointer-to-member can be thought of as a function from
29    /// `&T` to `&U` with matching lifetimes
30    ///
31    /// ```compile_fail
32    /// use field_offset::FieldOffset;
33    /// struct Foo<'a>(&'a str);
34    /// fn test<'a>(foo: &Foo<'a>, of: FieldOffset<Foo<'static>, &'static str>) -> &'static str {
35    ///     let of2 : FieldOffset<Foo<'a>, &'static str> = of; // This must not compile
36    ///     of2.apply(foo)
37    /// }
38    /// ```
39    /// That should compile:
40    /// ```
41    /// use field_offset::FieldOffset;
42    /// struct Foo<'a>(&'a str, &'static str);
43    /// fn test<'a>(foo: &'a Foo<'static>, of: FieldOffset<Foo, &'static str>) -> &'a str {
44    ///     let of2 : FieldOffset<Foo<'static>, &'static str> = of;
45    ///     of.apply(foo)
46    /// }
47    /// fn test2(foo: &Foo<'static>, of: FieldOffset<Foo, &'static str>) -> &'static str {
48    ///     let of2 : FieldOffset<Foo<'static>, &'static str> = of;
49    ///     of.apply(foo)
50    /// }
51    /// fn test3<'a>(foo: &'a Foo, of: FieldOffset<Foo<'a>, &'a str>) -> &'a str {
52    ///     of.apply(foo)
53    /// }
54    /// ```
55    PhantomData<(PhantomContra<T>, U, PinFlag)>,
56);
57
58/// `fn` cannot appear directly in a type that need to be const.
59/// Workaround that with an indirection
60struct PhantomContra<T>(fn(T));
61
62/// Type that can be used in the `PinFlag` parameter of `FieldOffset` to specify that
63/// this projection is valid on Pin types.
64/// See documentation of `FieldOffset::new_from_offset_pinned`
65pub enum AllowPin {}
66
67/// Type that can be used in the `PinFlag` parameter of `FieldOffset` to specify that
68/// this projection is not valid on Pin types.
69pub enum NotPinned {}
70
71impl<T, U> FieldOffset<T, U, NotPinned> {
72    // Use MaybeUninit to get a fake T
73    #[cfg(fieldoffset_maybe_uninit)]
74    #[inline]
75    fn with_uninit_ptr<R, F: FnOnce(*const T) -> R>(f: F) -> R {
76        let uninit = mem::MaybeUninit::<T>::uninit();
77        f(uninit.as_ptr())
78    }
79
80    // Use a dangling pointer to get a fake T
81    #[cfg(not(fieldoffset_maybe_uninit))]
82    #[inline]
83    fn with_uninit_ptr<R, F: FnOnce(*const T) -> R>(f: F) -> R {
84        f(mem::align_of::<T>() as *const T)
85    }
86
87    /// Construct a field offset via a lambda which returns a reference
88    /// to the field in question.
89    ///
90    /// # Safety
91    ///
92    /// The lambda *must not* dereference the provided pointer or access the
93    /// inner value in any way as it may point to uninitialized memory.
94    ///
95    /// For the returned `FieldOffset` to be safe to use, the returned pointer
96    /// must be valid for *any* instance of `T`. For example, returning a pointer
97    /// to a field from an enum with multiple variants will produce a `FieldOffset`
98    /// which is unsafe to use.
99    pub unsafe fn new<F: for<'a> FnOnce(*const T) -> *const U>(f: F) -> Self {
100        let offset = Self::with_uninit_ptr(|base_ptr| {
101            let field_ptr = f(base_ptr);
102            (field_ptr as usize).wrapping_sub(base_ptr as usize)
103        });
104
105        // Construct an instance using the offset
106        Self::new_from_offset(offset)
107    }
108    /// Construct a field offset directly from a byte offset.
109    ///
110    /// # Safety
111    ///
112    /// For the returned `FieldOffset` to be safe to use, the field offset
113    /// must be valid for *any* instance of `T`. For example, returning the offset
114    /// to a field from an enum with multiple variants will produce a `FieldOffset`
115    /// which is unsafe to use.
116    #[inline]
117    pub const unsafe fn new_from_offset(offset: usize) -> Self {
118        // Sanity check: ensure that the field offset plus the field size
119        // is no greater than the size of the containing struct. This is
120        // not sufficient to make the function *safe*, but it does catch
121        // obvious errors like returning a reference to a boxed value,
122        // which is owned by `T` and so has the correct lifetime, but is not
123        // actually a field.
124        #[cfg(fieldoffset_assert_in_const_fn)]
125        assert!(offset + mem::size_of::<U>() <= mem::size_of::<T>());
126        // On stable rust, we can still get an assert in debug mode,
127        // relying on the checked overflow behaviour
128        let _ = mem::size_of::<T>() - (offset + mem::size_of::<U>());
129
130        FieldOffset(offset, PhantomData)
131    }
132}
133
134// Methods for applying the pointer to member
135impl<T, U, PinFlag> FieldOffset<T, U, PinFlag> {
136    /// Apply the field offset to a native pointer.
137    #[inline]
138    pub fn apply_ptr(self, x: *const T) -> *const U {
139        ((x as usize) + self.0) as *const U
140    }
141    /// Apply the field offset to a native mutable pointer.
142    #[inline]
143    pub fn apply_ptr_mut(self, x: *mut T) -> *mut U {
144        ((x as usize) + self.0) as *mut U
145    }
146    /// Apply the field offset to a reference.
147    #[inline]
148    pub fn apply<'a>(self, x: &'a T) -> &'a U {
149        unsafe { &*self.apply_ptr(x) }
150    }
151    /// Apply the field offset to a mutable reference.
152    #[inline]
153    pub fn apply_mut<'a>(self, x: &'a mut T) -> &'a mut U {
154        unsafe { &mut *self.apply_ptr_mut(x) }
155    }
156    /// Get the raw byte offset for this field offset.
157    #[inline]
158    pub const fn get_byte_offset(self) -> usize {
159        self.0
160    }
161
162    // Methods for unapplying the pointer to member
163
164    /// Unapply the field offset to a native pointer.
165    ///
166    /// # Safety
167    ///
168    /// *Warning: very unsafe!*
169    ///
170    /// This applies a negative offset to a pointer. If the safety
171    /// implications of this are not already clear to you, then *do
172    /// not* use this method. Also be aware that Rust has stronger
173    /// aliasing rules than other languages, so it may be UB to
174    /// dereference the resulting pointer even if it points to a valid
175    /// location, due to the presence of other live references.
176    #[inline]
177    pub unsafe fn unapply_ptr(self, x: *const U) -> *const T {
178        ((x as usize) - self.0) as *const T
179    }
180    /// Unapply the field offset to a native mutable pointer.
181    ///
182    /// # Safety
183    ///
184    /// *Warning: very unsafe!*
185    ///
186    /// This applies a negative offset to a pointer. If the safety
187    /// implications of this are not already clear to you, then *do
188    /// not* use this method. Also be aware that Rust has stronger
189    /// aliasing rules than other languages, so it may be UB to
190    /// dereference the resulting pointer even if it points to a valid
191    /// location, due to the presence of other live references.
192    #[inline]
193    pub unsafe fn unapply_ptr_mut(self, x: *mut U) -> *mut T {
194        ((x as usize) - self.0) as *mut T
195    }
196    /// Unapply the field offset to a reference.
197    ///
198    /// # Safety
199    ///
200    /// *Warning: very unsafe!*
201    ///
202    /// This applies a negative offset to a reference. If the safety
203    /// implications of this are not already clear to you, then *do
204    /// not* use this method. Also be aware that Rust has stronger
205    /// aliasing rules than other languages, so this method may cause UB
206    /// even if the resulting reference points to a valid location, due
207    /// to the presence of other live references.
208    #[inline]
209    pub unsafe fn unapply<'a>(self, x: &'a U) -> &'a T {
210        &*self.unapply_ptr(x)
211    }
212    /// Unapply the field offset to a mutable reference.
213    ///
214    /// # Safety
215    ///
216    /// *Warning: very unsafe!*
217    ///
218    /// This applies a negative offset to a reference. If the safety
219    /// implications of this are not already clear to you, then *do
220    /// not* use this method. Also be aware that Rust has stronger
221    /// aliasing rules than other languages, so this method may cause UB
222    /// even if the resulting reference points to a valid location, due
223    /// to the presence of other live references.
224    #[inline]
225    pub unsafe fn unapply_mut<'a>(self, x: &'a mut U) -> &'a mut T {
226        &mut *self.unapply_ptr_mut(x)
227    }
228
229    /// Convert this offset to an offset that is allowed to go from `Pin<&T>`
230    /// to `Pin<&U>`
231    ///
232    /// # Safety
233    ///
234    /// The Pin safety rules for projection must be respected. These rules are
235    /// explained in the
236    /// [Pin documentation](https://doc.rust-lang.org/stable/std/pin/index.html#pinning-is-structural-for-field)
237    pub const unsafe fn as_pinned_projection(self) -> FieldOffset<T, U, AllowPin> {
238        FieldOffset::new_from_offset_pinned(self.get_byte_offset())
239    }
240
241    /// Remove the AllowPin flag
242    pub const fn as_unpinned_projection(self) -> FieldOffset<T, U, NotPinned> {
243        unsafe { FieldOffset::new_from_offset(self.get_byte_offset()) }
244    }
245}
246
247impl<T, U> FieldOffset<T, U, AllowPin> {
248    /// Construct a field offset directly from a byte offset, which can be projected from
249    /// a pinned.
250    ///
251    /// # Safety
252    ///
253    /// In addition to the safety rules of FieldOffset::new_from_offset, the projection
254    /// from `Pin<&T>` to `Pin<&U>` must also be allowed. The rules are explained in the
255    /// [Pin documentation](https://doc.rust-lang.org/stable/std/pin/index.html#pinning-is-structural-for-field)
256    #[inline]
257    pub const unsafe fn new_from_offset_pinned(offset: usize) -> Self {
258        FieldOffset(offset, PhantomData)
259    }
260
261    /// Apply the field offset to a pinned reference and return a pinned
262    /// reference to the field
263    #[inline]
264    pub fn apply_pin<'a>(self, x: Pin<&'a T>) -> Pin<&'a U> {
265        unsafe { x.map_unchecked(|x| self.apply(x)) }
266    }
267    /// Apply the field offset to a pinned mutable reference and return a
268    /// pinned mutable reference to the field
269    #[inline]
270    pub fn apply_pin_mut<'a>(self, x: Pin<&'a mut T>) -> Pin<&'a mut U> {
271        unsafe { x.map_unchecked_mut(|x| self.apply_mut(x)) }
272    }
273}
274
275impl<T, U> From<FieldOffset<T, U, AllowPin>> for FieldOffset<T, U, NotPinned> {
276    fn from(other: FieldOffset<T, U, AllowPin>) -> Self {
277        other.as_unpinned_projection()
278    }
279}
280
281/// Allow chaining pointer-to-members.
282///
283/// Applying the resulting field offset is equivalent to applying the first
284/// field offset, then applying the second field offset.
285///
286/// The requirements on the generic type parameters ensure this is a safe operation.
287impl<T, U, V> Add<FieldOffset<U, V>> for FieldOffset<T, U> {
288    type Output = FieldOffset<T, V>;
289    #[inline]
290    fn add(self, other: FieldOffset<U, V>) -> FieldOffset<T, V> {
291        FieldOffset(self.0 + other.0, PhantomData)
292    }
293}
294impl<T, U, V> Add<FieldOffset<U, V, AllowPin>> for FieldOffset<T, U, AllowPin> {
295    type Output = FieldOffset<T, V, AllowPin>;
296    #[inline]
297    fn add(self, other: FieldOffset<U, V, AllowPin>) -> FieldOffset<T, V, AllowPin> {
298        FieldOffset(self.0 + other.0, PhantomData)
299    }
300}
301impl<T, U, V> Add<FieldOffset<U, V>> for FieldOffset<T, U, AllowPin> {
302    type Output = FieldOffset<T, V>;
303    #[inline]
304    fn add(self, other: FieldOffset<U, V>) -> FieldOffset<T, V> {
305        FieldOffset(self.0 + other.0, PhantomData)
306    }
307}
308impl<T, U, V> Add<FieldOffset<U, V, AllowPin>> for FieldOffset<T, U> {
309    type Output = FieldOffset<T, V>;
310    #[inline]
311    fn add(self, other: FieldOffset<U, V, AllowPin>) -> FieldOffset<T, V> {
312        FieldOffset(self.0 + other.0, PhantomData)
313    }
314}
315
316/// The debug implementation prints the byte offset of the field in hexadecimal.
317impl<T, U, Flag> fmt::Debug for FieldOffset<T, U, Flag> {
318    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
319        write!(f, "FieldOffset({:#x})", self.0)
320    }
321}
322
323impl<T, U, Flag> Copy for FieldOffset<T, U, Flag> {}
324impl<T, U, Flag> Clone for FieldOffset<T, U, Flag> {
325    fn clone(&self) -> Self {
326        *self
327    }
328}
329
330/// This macro allows safe construction of a FieldOffset,
331/// by generating a known to be valid lambda to pass to the
332/// constructor. It takes a type, and the identifier of a field
333/// within that type as input.
334///
335/// Examples:
336///
337/// Offset of field `Foo.bar`
338///
339/// ```rust
340/// # #[macro_use]
341/// # extern crate field_offset;
342/// # fn main() {
343/// #[repr(C)]
344/// struct Foo { foo: i32, bar: i32 }
345/// assert_eq!(offset_of!(Foo => bar).get_byte_offset(), 4);
346/// # }
347/// ```
348///
349/// Offset of nested field `Foo.bar.x`
350///
351/// ```rust
352/// # #[macro_use]
353/// # extern crate field_offset;
354/// # fn main() {
355/// struct Bar { a: u8, x: u8 }
356/// struct Foo { foo: i32, bar: Bar }
357/// assert_eq!(offset_of!(Foo => bar: Bar => x).get_byte_offset(), 5);
358/// # }
359/// ```
360#[macro_export]
361macro_rules! offset_of {
362    ($t: path => $f: tt) => {{
363        // Construct the offset
364        #[allow(unused_unsafe)]
365        unsafe {
366            $crate::FieldOffset::<$t, _>::new(|x| {
367                $crate::__memoffset::raw_field!(x, $t, $f)
368            })
369        }
370    }};
371    ($t: path => $f: ident: $($rest: tt)*) => {
372        offset_of!($t => $f) + offset_of!($($rest)*)
373    };
374}
375
376#[cfg(test)]
377mod tests {
378    // Example structs
379    #[derive(Debug)]
380    struct Foo {
381        a: u32,
382        b: f64,
383        c: bool,
384    }
385
386    #[derive(Debug)]
387    struct Bar {
388        x: u32,
389        y: Foo,
390    }
391
392    #[derive(Debug)]
393    struct Tuple(i32, f64);
394
395    #[test]
396    fn test_simple() {
397        // Get a pointer to `b` within `Foo`
398        let foo_b = offset_of!(Foo => b);
399
400        // Construct an example `Foo`
401        let mut x = Foo {
402            a: 1,
403            b: 2.0,
404            c: false,
405        };
406
407        // Apply the pointer to get at `b` and read it
408        {
409            let y = foo_b.apply(&x);
410            assert_eq!(*y, 2.0);
411        }
412
413        // Apply the pointer to get at `b` and mutate it
414        {
415            let y = foo_b.apply_mut(&mut x);
416            *y = 42.0;
417        }
418        assert_eq!(x.b, 42.0);
419    }
420
421    #[test]
422    fn test_tuple() {
423        // Get a pointer to `b` within `Foo`
424        let tuple_1 = offset_of!(Tuple => 1);
425
426        // Construct an example `Foo`
427        let mut x = Tuple(1, 42.0);
428
429        // Apply the pointer to get at `b` and read it
430        {
431            let y = tuple_1.apply(&x);
432            assert_eq!(*y, 42.0);
433        }
434
435        // Apply the pointer to get at `b` and mutate it
436        {
437            let y = tuple_1.apply_mut(&mut x);
438            *y = 5.0;
439        }
440        assert_eq!(x.1, 5.0);
441    }
442
443    #[test]
444    fn test_nested() {
445        // Construct an example `Foo`
446        let mut x = Bar {
447            x: 0,
448            y: Foo {
449                a: 1,
450                b: 2.0,
451                c: false,
452            },
453        };
454
455        // Combine the pointer-to-members
456        let bar_y_b = offset_of!(Bar => y: Foo => b);
457
458        // Apply the pointer to get at `b` and mutate it
459        {
460            let y = bar_y_b.apply_mut(&mut x);
461            *y = 42.0;
462        }
463        assert_eq!(x.y.b, 42.0);
464    }
465
466    struct Parameterized<T, U> {
467        x: T,
468        _y: U,
469    }
470    #[test]
471    fn test_type_parameter() {
472        let _ = offset_of!(Parameterized<Parameterized<bool, bool>, bool> => x: Parameterized<bool, bool> => x);
473    }
474
475    #[test]
476    fn test_const() {
477        use crate::FieldOffset;
478        #[repr(C)]
479        struct SomeStruct {
480            a: u8,
481            b: u32,
482        }
483        const CONST_FIELD_OFFSET: FieldOffset<SomeStruct, u32> =
484            unsafe { FieldOffset::new_from_offset(4) };
485        const CONST_VALUE: usize = CONST_FIELD_OFFSET.get_byte_offset();
486        assert_eq!(offset_of!(SomeStruct => b).get_byte_offset(), CONST_VALUE);
487
488        static STATIC_FIELD_OFFSET: FieldOffset<SomeStruct, u32> =
489            unsafe { FieldOffset::new_from_offset(4) };
490        assert_eq!(
491            offset_of!(SomeStruct => b).get_byte_offset(),
492            STATIC_FIELD_OFFSET.get_byte_offset()
493        );
494    }
495
496    #[cfg(fieldoffset_has_alloc)]
497    #[test]
498    fn test_pin() {
499        use alloc::boxed::Box;
500        use core::pin::Pin;
501
502        // Get a pointer to `b` within `Foo`
503        let foo_b = offset_of!(Foo => b);
504        let foo_b_pin = unsafe { foo_b.as_pinned_projection() };
505        let foo = Box::pin(Foo {
506            a: 21,
507            b: 22.0,
508            c: true,
509        });
510        let pb: Pin<&f64> = foo_b_pin.apply_pin(foo.as_ref());
511        assert_eq!(*pb, 22.0);
512
513        let mut x = Box::pin(Bar {
514            x: 0,
515            y: Foo {
516                a: 1,
517                b: 52.0,
518                c: false,
519            },
520        });
521        let bar_y_b = offset_of!(Bar => y: Foo => b);
522        assert!(*bar_y_b.apply(&*x) == 52.0);
523
524        let bar_y_pin = unsafe { offset_of!(Bar => y).as_pinned_projection() };
525        *(bar_y_pin + foo_b_pin).apply_pin_mut(x.as_mut()) = 12.;
526        assert_eq!(x.y.b, 12.0);
527    }
528}