generic_mutability/genref.rs
1use core::borrow::{Borrow, BorrowMut};
2use core::fmt;
3use core::marker::PhantomData;
4use core::ops::{Deref, DerefMut};
5use core::ptr::NonNull;
6
7use crate::mutability::{IsMutable, IsShared, Mutability, Mutable, Shared};
8
9pub mod genref_methods;
10mod impl_traits;
11
12/// This is the main type of this crate.
13/// It is the generic-mutability equivalent of safe reference types (`&` and `&mut`).
14///
15/// # Usage for end users
16///
17/// *You need to interface with an API that is generic over mutability, but you do not use generic mutability to call it.*
18///
19/// You will need to create a `GenRef<'_, Mutable, T>` or `GenRef<'_, Shared, T>` via the `From<&mut T>` or `From<&T>` implementations, respectively.
20/// Then pass the `GenRef` to the function(s) you would like to call.
21/// At the end, you can use the resulting `GenRef` mostly the same way you would use a reference, but you can also unwrap it into a normal reference using the `into_mut` or `into_shared` functions.
22///
23/// For example:
24///
25/// ```
26/// # use core::ops::{Index, IndexMut};
27/// # use generic_mutability::{gen_mut, GenRef, Mutability, Shared};
28/// # fn gen_index<M: Mutability, I, C>(gen_collection: GenRef<'_, M, C>, index: I) -> GenRef<'_, M, C::Output>
29/// # where
30/// # C: Index<I> + IndexMut<I>,
31/// # {
32/// # gen_mut! {M => {
33/// # into_gen!(switch_shared_mut, index))
34/// # }}
35/// # }
36/// let v = vec![1, 2, 3];
37///
38/// let gen_r: GenRef<'_, Shared, i32> = gen_index(GenRef::from(&v), 1);
39///
40/// let r: &i32 = GenRef::into_shared(gen_r);
41///
42/// assert_eq!(gen_r, &2);
43/// assert_eq!(r, &2);
44/// ```
45///
46/// # Usage for libraries
47///
48/// *You are implementing an API that is supposed to be generic over mutability.*
49///
50/// You should start by introducing a *mutability parameter*.
51/// It is a generic argument (usually named `M`) with a `M: Mutability` bound on it .
52///
53/// Then you can take a `GenRef` as an argument.
54///
55/// `GenRef` always provides immutable access to the pointed-to value through the `Deref` trait.
56///
57/// Then, to map the generic reference into one of another type, you can do one of these:
58///
59/// - If the API you are calling has generic mutability accessors, you can pass the `GenRef` directly to them.
60/// Unlike normal references, which are automatically reborrowed, you may need to use `GenRef::reborrow` to perform a reborrow manually.
61/// You can also call `map_deref` to perform a dereference.
62///
63/// - If the API you're calling *does not* have generic mutability, you can use one of the following ways to unwrap and reconstruct the `GenRef`:
64/// - `map`
65/// - `field!` macro for accessing fields
66/// - `gen_mut!` macro
67/// - branch to cases on `Mutability::mutability()` and use `gen_{into,from}_{mut,shared}` with the proof provided by the return value of `Mutability::mutability()`.
68/// See the Examples section on how to do this.
69///
70/// # Examples
71///
72/// When you need to map a generic `GenRef` through an API that doesn't support it, you can use the following pattern:
73///
74/// ```
75/// # use generic_mutability::{ GenRef, Mutability, MutabilityEnum::* };
76/// fn gen_index<M: Mutability, T>(gen_slice: GenRef<'_, M, [T]>, index: usize) -> GenRef<'_, M, T> {
77/// match M::mutability() {
78/// Mutable(proof) => {
79/// let mut_slice: &mut [T] = GenRef::gen_into_mut(gen_slice, proof);
80/// let mut_elem: &mut T = &mut mut_slice[index];
81/// GenRef::gen_from_mut(mut_elem, proof)
82/// },
83/// Shared(proof) => {
84/// let ref_slice: &[T] = GenRef::gen_into_shared(gen_slice, proof);
85/// let ref_elem: &T = &ref_slice[index];
86/// GenRef::gen_from_shared(ref_elem, proof)
87/// }
88/// }
89/// }
90/// ```
91///
92/// In most cases, the mutable and shared cases share most of the code. When that is the case, it is often possible to use the `gen_mut!` macro, which expands to the same as above:
93///
94/// ```
95/// # use generic_mutability::{ GenRef, Mutability, gen_mut };
96/// fn gen_index<M: Mutability, T>(gen_slice: GenRef<'_, M, [T]>, index: usize) -> GenRef<'_, M, T> {
97/// gen_mut!(M => {
98/// let ref_slice = from_gen!(gen_slice);
99/// into_gen!(&gen ref_slice[index])
100/// })
101/// }
102/// ```
103///
104/// When performing a one-to-one mapping with two separate functions (often when calling into an API), the `GenRef::map` can come handy:
105///
106/// ```
107/// # use generic_mutability::{ GenRef, Mutability };
108/// fn gen_index<M: Mutability, T>(gen_slice: GenRef<'_, M, [T]>, index: usize) -> GenRef<'_, M, T> {
109/// GenRef::map(gen_slice, |r| &r[index], |r| &mut r[index])
110/// }
111/// ```
112///
113/// Finally, when accessing fields or performing indexing, the `field!` macro can be helpful as well:
114///
115/// ```
116/// # use generic_mutability::{ GenRef, Mutability, field };
117/// fn gen_index<M: Mutability, T>(gen_slice: GenRef<'_, M, [T]>, index: usize) -> GenRef<'_, M, T> {
118/// field!(&gen gen_slice[index])
119/// }
120/// ```
121#[repr(transparent)]
122pub struct GenRef<'s, M: Mutability, T: ?Sized> {
123 // This could contain an `ErasedMutRef` instead of `_lifetime` and `ptr`,
124 // but that way it could not implement `Copy`
125 _lifetime: PhantomData<&'s mut T>,
126 _mutability: PhantomData<*const M>,
127 ptr: NonNull<T>,
128}
129
130macro_rules! docs_for {
131 (as_ptr) => {
132 "Casts the reference into a `NonNull` pointer.
133
134# Safety
135
136The `GenRef` must not be used while the pointer is active.
137The exact semantics of this depend on the memory model adopted by Rust.
138
139# Guarantees
140
141The returned pointer is guaranteed to be valid for reads for `'s`, and also for writes if `M` is `Mutable`."
142 };
143 (gen_into_shared_downgrading) => {
144 "Converts a generic `GenRef<'_, M, T>` into `&T`, downgrading the reference if `M` is `Mutable`.
145
146If `M` is `Shared` it behaves exactly the same way as `gen_into_shared` without requiring a proof for sharedness.
147In this case, the difference between the two is purely semantic: if you have proof that `M` is `Shared`, you should use `gen_into_shared`."
148 };
149 (gen_into_mut) => {
150 "Converts a generic `GenRef<'_, M, T>` into `&mut T`.
151This is available in a generic context.
152
153Once the transformations are done, the result can be converted back into a `GenRef` using the `gen_from_mut` function.
154
155The conversion requires that `M` is `Mutable`, this must be proven by passing an `IsMutable<M>` value.
156That can be obtained by `match`ing on `M::mutability()`."
157 };
158 (gen_into_shared) => {
159 "Converts a generic `GenRef<'_, M, T>` into `&T`.
160This is available in a generic context.
161
162Once the transformations are done, the result can be converted back into a `GenRef` using the `gen_from_shared` function.
163
164The conversion requires that `M` is `Shared`, this must be proven by passing an `IsShared<M>` value.
165That can be obtained by `match`ing on `M::mutability()`.
166
167If you want to force the conversion even if `M` is `Mutable`, you can use the `gen_into_shared_downgrading` function."
168 };
169 (reborrow) => {
170 "Generically reborrows a `GenRef`.
171That is, it creates a shorter-lived owned `GenRef` from a `&mut GenRef`.
172This is available in a generic context.
173
174This requires the variable to be marked `mut`, even if `M` is `Shared` and thus no mutation takes place."
175 };
176 (map) => {
177 "Maps a generic `GenRef` into another one using either `f_mut` or `f_shared`.
178This is available in a generic context.
179
180Using this function is usually sufficient.
181For mapping over field access, you can use the `field!` macro instead.
182If you need more flexibility, you can use the `gen_mut!` macro or `match`ing over `M::mutability()`."
183 };
184 (map_deref) => {
185 "Generically dereferences the value contained in the `GenRef`.
186This is available in a generic context."
187 };
188}
189
190pub(self) use docs_for;
191
192impl<'s, M: Mutability, T: ?Sized> GenRef<'s, M, T> {
193 #[inline(always)]
194 /// Creates a `GenRef<'s, M, T>` from a pointer with the chosen mutability `M` and lifetime `'s`, without checking.
195 ///
196 /// To create a non-generic `GenRef` from a reference, use the `From` implementation.
197 ///
198 /// To convert a reference into a `GenRef` in a generic context, use the `gen_from_shared` and `gen_from_mut` methods with a proof or the `gen_from_mut_downgrading` method instead.
199 ///
200 /// # Safety
201 ///
202 /// `GenRef` is a safe reference type. Using this method is equivalent to dereferencing the pointer and creating a reference from it. As such:
203 ///
204 /// - The pointer must be properly aligned.
205 /// - The pointer must point to an initialized instance of `T`.
206 /// - The lifetime `'s` and mutability `M` are arbitrarily chosen and do not necessarily reflect the actual lifetime and mutability of the data.
207 /// Extra care must be taken to ensure that the correct lifetime and mutability parameters are used.
208 /// - Furthermore:
209 /// - If the mutability is `Immutable`:
210 /// - The pointer must be valid for reads for lifetime `'s`.
211 /// - The pointed-to value must not be written to by other pointers and no mutable references to it may exist during `'s`.
212 /// - If the mutability is `Mutable`:
213 /// - The pointer must be valid for reads and writes for lifetime `'s`.
214 /// - The pointed-to value must not be accessed (read or written) by other pointers, and no other references to it may exist during `'s`.
215 pub unsafe fn from_ptr_unchecked(ptr: NonNull<T>) -> Self {
216 Self {
217 _lifetime: PhantomData,
218 _mutability: PhantomData,
219 ptr,
220 }
221 }
222
223 #[inline(always)]
224 #[doc = docs_for!(as_ptr)]
225 pub fn as_ptr(genref: &Self) -> NonNull<T> {
226 genref.ptr
227 }
228
229 #[inline(always)]
230 /// Converts a `&mut T` into a generic `GenRef<'_, M, T>`, downgrading the reference if `M` is `Shared`.
231 ///
232 /// If `M` is `Mutable` it behaves exactly the same way as `gen_from_mut` without requiring a proof for mutability.
233 /// In this case, the difference between the two is purely semantic: if you have proof that `M` is `Mutable`, you should use `gen_from_mut`.
234 pub fn gen_from_mut_downgrading(reference: &'s mut T) -> Self {
235 let ptr = NonNull::from(reference);
236
237 // SAFETY: `ptr` is derived from a mutable reference, so points to a valid `T` and is valid and unaliased for `'s`.
238 // The correct lifetime is enforced by the function signature.
239 unsafe { Self::from_ptr_unchecked(ptr) }
240 }
241
242 #[inline(always)]
243 #[doc = docs_for!(gen_into_shared_downgrading)]
244 pub fn gen_into_shared_downgrading(genref: Self) -> &'s T {
245 let ptr = GenRef::as_ptr(&genref);
246
247 // SAFETY: `GenRef::as_ptr` guarantees that `ptr` points to a valid `T` and is valid for reads for `'s`.
248 unsafe { ptr.as_ref() }
249 }
250
251 #[inline(always)]
252 #[doc = docs_for!(gen_into_mut)]
253 pub fn gen_into_mut(genref: Self, _proof: IsMutable<M>) -> &'s mut T {
254 let mut ptr = GenRef::as_ptr(&genref);
255
256 // SAFETY: For a value of `IsMutable<M>` to exist, `M` must be `Mutable`.
257 // `GenRef::as_ptr` guarantees that `ptr` points to a valid `T` and (given that `M` is `Mutable`) is valid for reads and writes for `'s`.
258 // This function takes ownership of `GenRef`, so `ptr` can not be aliased.
259 unsafe { ptr.as_mut() }
260 }
261 #[inline(always)]
262 /// Converts a `&mut T` into a generic `GenRef<'_, M, T>`.
263 /// This is available in a generic context.
264 ///
265 /// The conversion requires that `M` is `Mutable`, this must be proven by passing an `IsMutable<M>` value.
266 /// That can be obtained by `match`ing on `M::mutability()`.
267 ///
268 /// If you want to force the conversion even if `M` is `Shared`, you can use the `gen_from_mut_downgrading` function.
269 pub fn gen_from_mut(reference: &'s mut T, _proof: IsMutable<M>) -> Self {
270 // `gen_from_mut_downgrading` is semantically different, but when called with `M = Mutable` they perform the same operation.
271 GenRef::gen_from_mut_downgrading(reference)
272 }
273
274 #[inline(always)]
275 #[doc = docs_for!(gen_into_shared)]
276 pub fn gen_into_shared(genref: Self, _proof: IsShared<M>) -> &'s T {
277 // `gen_into_shared_downgrading` is semantically different, but when called with `M = Shared` they perform the same operation.
278 GenRef::gen_into_shared_downgrading(genref)
279 }
280
281 #[inline(always)]
282 /// Converts a `&T` into a generic `GenRef<'_, M, T>`.
283 /// This is available in a generic context.
284 ///
285 /// The conversion requires that `M` is `Shared`, this must be proven by passing an `IsShared<M>` value.
286 /// That can be obtained by `match`ing on `M::mutability()`.
287 pub fn gen_from_shared(reference: &'s T, _proof: IsShared<M>) -> Self {
288 let ptr = NonNull::from(reference);
289
290 // SAFETY: For a value of `IsShared<M>` to exist, `M` must be `Shared`. `ptr` is derived from a shared reference, so it is valid for reads for `'s`.
291 // The correct lifetime is enforced by the function signature.
292 unsafe { Self::from_ptr_unchecked(ptr) }
293 }
294
295 #[inline(always)]
296 #[doc = docs_for!(reborrow)]
297 pub fn reborrow(genref: &mut Self) -> GenRef<'_, M, T> {
298 // SAFETY: `GenRef::as_ptr` guarantees that `ptr` points to a valid `T` and is valid for reads for `'_`. If `M` is `Mutable`, it also guarantees validity for writes.
299 // The `GenRef` received as argument is not used after calling `as_ptr` for the lifetime `'_`, and the pointer returned by `as_ptr` can not be aliased, both guaranteed by the exclusive reference received as argument.
300 // The correct lifetime and mutability parameters are enforced by the function signature.
301 unsafe { GenRef::from_ptr_unchecked(GenRef::as_ptr(genref)) }
302 }
303
304 #[inline(always)]
305 #[doc = docs_for!(map)]
306 pub fn map<U: ?Sized>(
307 genref: Self,
308 f_shared: impl FnOnce(&T) -> &U,
309 f_mut: impl FnOnce(&mut T) -> &mut U,
310 ) -> GenRef<'s, M, U> {
311 use crate::MutabilityEnum::*;
312
313 match M::mutability() {
314 Mutable(proof) => {
315 GenRef::gen_from_mut(f_mut(GenRef::gen_into_mut(genref, proof)), proof)
316 }
317 Shared(proof) => {
318 GenRef::gen_from_shared(f_shared(GenRef::gen_into_shared(genref, proof)), proof)
319 }
320 }
321 }
322
323 #[inline(always)]
324 #[doc = docs_for!(map_deref)]
325 pub fn map_deref(genref: Self) -> GenRef<'s, M, T::Target>
326 where
327 T: Deref + DerefMut,
328 {
329 GenRef::map(genref, Deref::deref, DerefMut::deref_mut)
330 }
331}
332
333impl<'s, T: ?Sized> GenRef<'s, Shared, T> {
334 #[inline(always)]
335 /// Converts a `GenRef<'_, Shared, T>` into `&T` in a non-generic context.
336 ///
337 /// This is used to unwrap the reference in end-user code.
338 ///
339 /// To perform the same operation in a generic context, use `gen_into_shared` or `gen_into_shared_downgrading`.
340 pub fn into_shared(genref: Self) -> &'s T {
341 Self::gen_into_shared(genref, Shared::mutability())
342 }
343}
344impl<'s, T: ?Sized> GenRef<'s, Mutable, T> {
345 #[inline(always)]
346 /// Converts a `GenRef<'_, Mutable T>` into `&mut T` in a non-generic context.
347 ///
348 /// This is used to unwrap the reference in end-user code.
349 ///
350 /// To perform the same operation in a generic context, use `gen_into_mut`.
351 pub fn into_mut(genref: Self) -> &'s mut T {
352 Self::gen_into_mut(genref, Mutable::mutability())
353 }
354}
355
356/// Creates a non-generic `GenRef<'_, Shared, T>` from a `&T`.
357///
358/// This is used to create a `GenRef` in end-user code.
359///
360/// To create a generic `GenRef<'_, M, T>`, use `gen_from_shared`.
361impl<'s, T: ?Sized> From<&'s T> for GenRef<'s, Shared, T> {
362 #[inline(always)]
363 fn from(reference: &'s T) -> Self {
364 GenRef::gen_from_shared(reference, Shared::mutability())
365 }
366}
367/// Creates a non-generic `GenRef<'_, Mutable, T>` from a `&mut T`.
368///
369/// This is used to create a `GenRef` in end-user code.
370///
371/// To create a generic `GenRef<'_, M, T>`, use `gen_from_mut` or `gen_from_mut_downgrading`.
372impl<'s, T: ?Sized> From<&'s mut T> for GenRef<'s, Mutable, T> {
373 #[inline(always)]
374 fn from(reference: &'s mut T) -> Self {
375 GenRef::gen_from_mut(reference, Mutable::mutability())
376 }
377}
378
379/// This is available in a generic context.
380impl<M: Mutability, T: ?Sized> Deref for GenRef<'_, M, T> {
381 type Target = T;
382 #[inline(always)]
383 fn deref(&self) -> &Self::Target {
384 let ptr = GenRef::as_ptr(self);
385
386 // SAFETY: `GenRef::as_ptr` guarantees that `ptr` points to a valid `T` and is valid for reads for `'s`.
387 // The correct lifetime is enforced by the function signature.
388 unsafe { ptr.as_ref() }
389 }
390}
391
392/// This is only implemented for `GenRef<'_, Mutable, T>`, and is not available in a generic context.
393///
394/// To get mutable access to the value, call `reborrow` followed by `gen_into_mut` (this requires proving that `M` is mutable).
395impl<T: ?Sized> DerefMut for GenRef<'_, Mutable, T> {
396 #[inline(always)]
397 fn deref_mut(&mut self) -> &mut Self::Target {
398 GenRef::into_mut(GenRef::reborrow(self))
399 }
400}
401
402/// This is only implemented for `GenRef<'_, Shared, T>`, and is not available in a generic context.
403impl<T: ?Sized> Clone for GenRef<'_, Shared, T> {
404 /// Copies the reference. This does not call `clone` on the pointed-to value.
405 #[inline(always)]
406 fn clone(&self) -> Self {
407 *self
408 }
409}
410/// This is only implemented for `GenRef<'_, Shared, T>`, and is not available in a generic context.
411impl<T: ?Sized> Copy for GenRef<'_, Shared, T> {}
412
413impl<M: Mutability, T: ?Sized> Borrow<T> for GenRef<'_, M, T> {
414 #[inline(always)]
415 fn borrow(&self) -> &T {
416 self
417 }
418}
419/// This is only implemented for `GenRef<'_, Mutable, T>`, and is not available in a generic context.
420impl<T: ?Sized> BorrowMut<T> for GenRef<'_, Mutable, T> {
421 #[inline(always)]
422 fn borrow_mut(&mut self) -> &mut T {
423 self
424 }
425}
426
427impl<M: Mutability, T: ?Sized> fmt::Pointer for GenRef<'_, M, T> {
428 #[inline(always)]
429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430 self.ptr.fmt(f)
431 }
432}
433
434/// This implementation requires `T: Sync` even when `M` is `Mutable`.
435/// With specialisation, this requirement could be lifted.
436// SAFETY: `GenRef` behaves like a reference, and both `&T` and `&mut T` implement `Send` if `T` is `Send` and `Sync`
437unsafe impl<M: Mutability, T: ?Sized> Send for GenRef<'_, M, T> where T: Send + Sync {}
438// SAFETY: `GenRef` behaves like a reference, and both `&T` and `&mut T` implement `Sync` if `T` is `Sync`
439unsafe impl<M: Mutability, T: ?Sized> Sync for GenRef<'_, M, T> where T: Sync {}