ghost_cell/
ghost_borrow.rs

1//! The `GhostBorrow` trait allows simultaneously borrowing multiple `GhostCell` immutably.
2//!
3//! Technically, this is already allowed, however it can be useful to do so as a single expression, which this trait
4//! provides.
5
6use core::mem;
7use core::ptr;
8
9use crate::ghost_cell::*;
10
11/// A trait for implementing multiple borrows for any number of arguments, using a `GhostToken<'a, 'brand>`.
12///
13/// Implemented for a mixture of tuple and array types.
14pub trait GhostBorrow<'a, 'brand> {
15    /// The references you get as a result.
16    ///
17    /// For example, if `Self` is `(&'a GhostCell<'brand, T0>, &'a GhostCell<'brand, T1>)` then `Result` is
18    /// `(&'a T0, &'a T1)`.
19    type Result;
20
21    /// Borrows any number of `GhostCell`s at the same time.
22    ///
23    /// #   Example
24    ///
25    /// ```rust
26    /// use ghost_cell::{GhostToken, GhostCell, GhostBorrow};
27    ///
28    /// let value = GhostToken::new(|mut token| {
29    ///     let cell1 = GhostCell::new(42);
30    ///     let cell2 = GhostCell::new(47);
31    ///
32    ///     let (reference1, reference2): (&i32, &i32) = (&cell1, &cell2).borrow(&token);
33    ///
34    ///     (*reference1, *reference2)
35    /// });
36    ///
37    /// assert_eq!((42, 47), value);
38    /// ```
39    fn borrow(self, token: &'a GhostToken<'brand>) -> Self::Result;
40}
41
42impl<'a, 'brand, T> GhostBorrow<'a, 'brand> for &'a [GhostCell<'brand, T>] {
43    type Result = &'a [T];
44
45    fn borrow(self, _: &'a GhostToken<'brand>) -> Self::Result {
46        //  Safety:
47        //  -   Shared access to the `GhostToken` ensures shared access to the cells' content.
48        //  -   `GhostCell` is `repr(transparent)`, hence `T` and `GhostCell<T>` have the same memory representation.
49        unsafe { mem::transmute::<Self, Self::Result>(self) }
50    }
51}
52
53impl<'a, 'brand, T, const N: usize> GhostBorrow<'a, 'brand> for &'a [GhostCell<'brand, T>; N] {
54    type Result = &'a [T; N];
55
56    fn borrow(self, _: &'a GhostToken<'brand>) -> Self::Result {
57        //  Safety:
58        //  -   Shared access to the `GhostToken` ensures shared access to the cells' content.
59        //  -   `GhostCell` is `repr(transparent)`, hence `T` and `GhostCell<T>` have the same memory representation.
60        unsafe { mem::transmute::<Self, Self::Result>(self) }
61    }
62}
63
64impl<'a, 'brand, T: ?Sized, const N: usize> GhostBorrow<'a, 'brand> for [&'a GhostCell<'brand, T>; N] {
65    type Result = [&'a T; N];
66
67    fn borrow(self, _: &'a GhostToken<'brand>) -> Self::Result {
68        //  Safety:
69        //  -   `[&'a GhostCell<'brand, T>; N]` and `[&'a T; N]` have the same size.
70        //  -   `[&'a GhostCell<'brand, T>; N]` implements `Copy`, so no `mem::forget` is needed.
71        //  -   We can't use `mem::transmute`, because of https://github.com/rust-lang/rust/issues/61956.
72        unsafe { ptr::read(&self as *const _ as *const Self::Result) }
73    }
74}
75
76macro_rules! last {
77    () => {};
78    ($head:ident $(,)?) => {
79        $head
80    };
81    ($head:ident, $($tail:ident),+ $(,)?) => {
82        last!($($tail),+)
83    };
84}
85
86macro_rules! generate_public_instance {
87    ( $($name:ident),* ; $($type_letter:ident),* ) => {
88        impl<'a, 'brand, $($type_letter: ?Sized,)*> GhostBorrow<'a, 'brand>
89            for ( $(&'a GhostCell<'brand, $type_letter>, )* )
90        {
91            type Result = ( $(&'a $type_letter, )* );
92
93            fn borrow(self, token: &'a GhostToken<'brand>) -> Self::Result {
94                let ($($name,)*) = self;
95
96                ( $( $name.borrow(token),)* )
97            }
98        }
99
100        impl<'a, 'brand, $($type_letter,)*> GhostBorrow<'a, 'brand>
101            for &'a ( $(GhostCell<'brand, $type_letter>, )* )
102        where
103            last!( $($type_letter),* ): ?Sized
104        {
105            type Result = &'a ( $($type_letter, )* );
106
107            fn borrow(self, _: &'a GhostToken<'brand>) -> Self::Result {
108                //  Safety:
109                //  -   Exclusive access to the `GhostToken` ensures exclusive access to the cells' content.
110                //  -   `GhostCell` is `repr(transparent)`, hence `T` and `GhostCell<T>` have the same memory representation.
111                unsafe { core::mem::transmute::<Self, Self::Result>(self) }
112            }
113        }
114    };
115}
116
117generate_public_instance!(a ; T0);
118generate_public_instance!(a, b ; T0, T1);
119generate_public_instance!(a, b, c ; T0, T1, T2);
120generate_public_instance!(a, b, c, d ; T0, T1, T2, T3);
121generate_public_instance!(a, b, c, d, e ; T0, T1, T2, T3, T4);
122generate_public_instance!(a, b, c, d, e, f ; T0, T1, T2, T3, T4, T5);
123generate_public_instance!(a, b, c, d, e, f, g ; T0, T1, T2, T3, T4, T5, T6);
124generate_public_instance!(a, b, c, d, e, f, g, h ; T0, T1, T2, T3, T4, T5, T6, T7);
125generate_public_instance!(a, b, c, d, e, f, g, h, i ; T0, T1, T2, T3, T4, T5, T6, T7, T8);
126generate_public_instance!(a, b, c, d, e, f, g, h, i, j ; T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
127generate_public_instance!(a, b, c, d, e, f, g, h, i, j, k ; T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, TA);
128generate_public_instance!(a, b, c, d, e, f, g, h, i, j, k, l ; T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB);
129
130#[cfg(test)]
131mod tests {
132
133    use super::*;
134
135    #[test]
136    fn multiple_borrows_tuple() {
137        let value = GhostToken::new(|token| {
138            let cell1 = GhostCell::new(42);
139            let cell2 = GhostCell::new(47);
140            let cell3 = GhostCell::new(7);
141            let cell4 = GhostCell::new(9);
142
143            let (reference1, reference2, reference3, reference4): (&i32, &i32, &i32, &i32) =
144                (&cell1, &cell2, &cell3, &cell4).borrow(&token);
145
146            (*reference1, *reference2, *reference3, *reference4)
147        });
148        assert_eq!((42, 47, 7, 9), value);
149    }
150
151    #[test]
152    fn multiple_borrows_tuple_ref() {
153        let value = GhostToken::new(|token| {
154            let cell1 = GhostCell::new(42);
155            let cell2 = GhostCell::new(47);
156            let cell3 = GhostCell::new(7);
157            let cell4 = GhostCell::new(9);
158            let tuple = (cell1, cell2, cell3, cell4);
159
160            let reference: &(i32, i32, i32, i32) = tuple.borrow(&token);
161
162            (reference.0, reference.1, reference.2, reference.3)
163        });
164        assert_eq!((42, 47, 7, 9), value);
165    }
166
167    #[test]
168    fn multiple_borrows_array_ref() {
169        let value = GhostToken::new(|token| {
170            let cell1 = GhostCell::new(42);
171            let cell2 = GhostCell::new(47);
172            let cell3 = GhostCell::new(7);
173            let cell4 = GhostCell::new(9);
174            let array = [cell1, cell2, cell3, cell4];
175
176            let reference: &[i32; 4] = array.borrow(&token);
177
178            (reference[0], reference[1], reference[2], reference[3])
179        });
180        assert_eq!((42, 47, 7, 9), value);
181    }
182
183    #[test]
184    fn multiple_borrows_tuple_unsized() {
185        let value = GhostToken::new(|token| {
186            let mut data1 = 42;
187            let mut data2 = [47];
188            let mut data3 = 7;
189            let mut data4 = [9];
190
191            let cell1 = &*GhostCell::from_mut(&mut data1 as &mut dyn ToString);
192            let cell2 = &*GhostCell::from_mut(&mut data2 as &mut [i32]);
193            let cell3 = &*GhostCell::from_mut(&mut data3 as &mut dyn ToString);
194            let cell4 = &*GhostCell::from_mut(&mut data4 as &mut [i32]);
195
196            let (reference1, reference2, reference3, reference4) = (cell1, cell2, cell3, cell4).borrow(&token);
197
198            (
199                reference1.to_string(),
200                reference2[0],
201                reference3.to_string(),
202                reference4[0],
203            )
204        });
205        assert_eq!(("42".to_owned(), 47, "7".to_owned(), 9), value);
206    }
207
208    #[test]
209    fn multiple_borrows_array_unsized_slice() {
210        let value = GhostToken::new(|token| {
211            let mut data1 = [42];
212            let mut data2 = [47];
213            let mut data3 = [7];
214            let mut data4 = [9];
215
216            let cell1 = &*GhostCell::from_mut(&mut data1 as &mut [i32]);
217            let cell2 = &*GhostCell::from_mut(&mut data2 as &mut [i32]);
218            let cell3 = &*GhostCell::from_mut(&mut data3 as &mut [i32]);
219            let cell4 = &*GhostCell::from_mut(&mut data4 as &mut [i32]);
220            let array = [cell1, cell2, cell3, cell4];
221
222            let reference: [&[i32]; 4] = array.borrow(&token);
223
224            reference.map(|slice| slice[0])
225        });
226        assert_eq!([42, 47, 7, 9], value);
227    }
228
229    #[test]
230    fn multiple_borrows_array_unsized_dyn_trait() {
231        let value = GhostToken::new(|token| {
232            let mut data1 = 42;
233            let mut data2 = 47;
234            let mut data3 = 7;
235            let mut data4 = 9;
236
237            let cell1 = &*GhostCell::from_mut(&mut data1 as &mut dyn ToString);
238            let cell2 = &*GhostCell::from_mut(&mut data2 as &mut dyn ToString);
239            let cell3 = &*GhostCell::from_mut(&mut data3 as &mut dyn ToString);
240            let cell4 = &*GhostCell::from_mut(&mut data4 as &mut dyn ToString);
241            let array = [cell1, cell2, cell3, cell4];
242
243            let reference: [&dyn ToString; 4] = array.borrow(&token);
244
245            reference.map(ToString::to_string)
246        });
247        assert_eq!(
248            ["42".to_owned(), "47".to_owned(), "7".to_owned(), "9".to_owned()],
249            value
250        );
251    }
252} // mod tests