Skip to main content

const_field_offset/
lib.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4/*!
5This crate provides the [`FieldOffsets`] derive macro to obtain constant
6[`FieldOffset`]s for fields of a `#[repr(C)]` struct.
7
8The [`FieldOffset`] type is re-exported from the `field-offset` crate.
9
10## Usage
11
12```rust
13use const_field_offset::{FieldOffsets, FieldOffset};
14
15#[derive(FieldOffsets)]
16#[repr(C)]
17struct Foo { a: u32, b: f64 }
18
19let foo_b: FieldOffset<Foo, f64> = Foo::FIELD_OFFSETS.b();
20assert_eq!(*foo_b.apply(&Foo { a: 1, b: 42.0 }), 42.0);
21```
22
23The `FIELD_OFFSETS` constant is a zero-sized type with a const fn method
24per field. Each method returns the corresponding [`FieldOffset`].
25
26For pin-projecting offsets, use `#[pin]`:
27
28```rust
29use const_field_offset::{FieldOffsets, FieldOffset, AllowPin};
30
31#[derive(FieldOffsets)]
32#[repr(C)]
33#[pin]
34struct Foo { a: u32, b: f64 }
35
36let foo_b: FieldOffset<Foo, f64, AllowPin> = Foo::FIELD_OFFSETS.b();
37let pinned = Box::pin(Foo { a: 1, b: 42.0 });
38assert_eq!(*foo_b.apply_pin(pinned.as_ref()), 42.0);
39```
40
41The `#[pin]` attribute enforces that the struct is `!Unpin` and does not
42implement `Drop` (use `#[pin_drop]` with [`PinnedDrop`] instead).
43*/
44#![no_std]
45
46#[cfg(test)]
47extern crate alloc;
48
49use core::pin::Pin;
50
51#[doc(inline)]
52pub use const_field_offset_macro::FieldOffsets;
53
54pub use field_offset::{AllowPin, FieldOffset, NotPinned};
55
56/// This trait needs to be implemented if you use the `#[pin_drop]` attribute. It enables
57/// you to implement Drop for your type safely.
58pub trait PinnedDrop {
59    /// This is the equivalent to the regular Drop trait with the difference that self
60    /// is pinned.
61    fn drop(self: Pin<&mut Self>);
62
63    #[doc(hidden)]
64    fn do_safe_pinned_drop(&mut self) {
65        let p = unsafe { Pin::new_unchecked(self) };
66        p.drop()
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use crate as const_field_offset;
74
75    #[derive(Debug, FieldOffsets)]
76    #[repr(C)]
77    struct Foo {
78        a: u32,
79        b: f64,
80        c: bool,
81    }
82
83    #[derive(Debug, FieldOffsets)]
84    #[repr(C)]
85    struct Bar {
86        x: u32,
87        y: Foo,
88    }
89
90    #[test]
91    #[allow(clippy::float_cmp)]
92    fn test_simple() {
93        let foo_b = Foo::FIELD_OFFSETS.b();
94        let mut x = Foo { a: 1, b: 2.0, c: false };
95        assert_eq!(*foo_b.apply(&x), 2.0);
96        *foo_b.apply_mut(&mut x) = 42.0;
97        assert_eq!(x.b, 42.0);
98    }
99
100    #[test]
101    #[allow(clippy::float_cmp)]
102    fn test_nested() {
103        let mut x = Bar { x: 0, y: Foo { a: 1, b: 2.0, c: false } };
104        let bar_y_b = Bar::FIELD_OFFSETS.y() + Foo::FIELD_OFFSETS.b();
105        *bar_y_b.apply_mut(&mut x) = 42.0;
106        assert_eq!(x.y.b, 42.0);
107    }
108
109    #[test]
110    #[allow(clippy::float_cmp)]
111    fn test_pin() {
112        use alloc::boxed::Box;
113        let foo_b = Foo::FIELD_OFFSETS.b();
114        let foo_b_pin = unsafe { foo_b.as_pinned_projection() };
115        let foo_object = Box::pin(Foo { a: 21, b: 22.0, c: true });
116        let pb: Pin<&f64> = foo_b_pin.apply_pin(foo_object.as_ref());
117        assert_eq!(*pb, 22.0);
118
119        let mut x = Box::pin(Bar { x: 0, y: Foo { a: 1, b: 52.0, c: false } });
120        let bar_y_b = Bar::FIELD_OFFSETS.y() + foo_b_pin;
121        assert_eq!(*bar_y_b.apply(&*x), 52.0);
122
123        let bar_y_pin = unsafe { Bar::FIELD_OFFSETS.y().as_pinned_projection() };
124        *(bar_y_pin + foo_b_pin).apply_pin_mut(x.as_mut()) = 12.;
125        assert_eq!(x.y.b, 12.0);
126    }
127
128    // Verify FIELD_OFFSETS is const-evaluable
129    const _CONST_FOO_B: FieldOffset<Foo, f64> = Foo::FIELD_OFFSETS.b();
130    const _CONST_BAR_Y: FieldOffset<Bar, Foo> = Bar::FIELD_OFFSETS.y();
131}
132
133/**
134Test that one can't implement Unpin for pinned struct
135
136This should work:
137
138```rust
139use const_field_offset::FieldOffsets;
140#[repr(C)]
141#[derive(FieldOffsets)]
142struct Foo {
143    x: u32,
144}
145impl Unpin for Foo {}
146```
147
148But not this:
149```compile_fail
150use const_field_offset::FieldOffsets;
151#[repr(C)]
152#[derive(FieldOffsets)]
153#[pin]
154struct Foo {
155    x: u32,
156}
157impl Unpin for Foo {}
158```
159*/
160#[cfg(doctest)]
161const NO_IMPL_UNPIN: u32 = 0;