loaned/
loaned.rs

1use crate::*;
2use core::{
3  fmt::{Debug, Display},
4  hash::Hash,
5};
6
7/// `Loaned<'t, T>` connotes ownership of a value `T`, with the caveat that
8/// allocations owned by it are immutably loaned for `'t` (i.e. something else
9/// may hold an `&'t` reference to such allocations).
10///
11/// Thus, for the duration of `'t`, one cannot mutably access this value.
12/// However, unlike [`LoanedMut`], one can immutably access it.
13///
14/// One can store this value somewhere with `Loaned::place`, which will ensure
15/// that it cannot be used for the duration of `'t`.
16///
17/// Taking the value out of a [`Loaned`] can be done with the [`take!`] macro,
18/// which will statically ensure that `'t` has expired.
19///
20/// # Dropping
21///
22/// The value held by a `Loaned` can only be dropped once `'t` expires. Since
23/// there is no way in the type system to enforce this, nor any way to check
24/// this at runtime, dropping a `Loaned` panics.
25///
26/// If leaking is intentional, use a `ManuallyDrop<Loaned<'t, T>>`.
27///
28/// To drop the inner value, use the [`drop!`] macro, which will statically
29/// ensure that `'t` has expired.
30#[must_use = "dropping a `Loaned` panics; use `loaned::drop!` instead"]
31#[repr(transparent)]
32pub struct Loaned<'t, T> {
33  /// Invariant: the target of `inner` is borrowed for `'t`, so it may only be
34  /// accessed immutably (not mutably or uniquely) for the duration of `'t`.
35  pub(crate) inner: RawLoaned<T>,
36  pub(crate) _contravariant: PhantomData<fn(&'t ())>,
37}
38
39/// Like `&T`, `Loaned<T>` is only `Send` if `T` is `Sync`.
40///
41/// Otherwise, code could cause data races:
42///
43/// ```rust,compile_fail E0277
44/// use loaned::Loaned;
45/// use std::cell::Cell;
46/// let x = Loaned::new(Box::new(Cell::new(123)));
47/// let y = x.borrow();
48/// let x = std::thread::scope(|s| {
49///   let h = s.spawn(move || {
50///     x.set(456); // <- unsynchronized write
51///     x
52///   });
53///   y.get(); // // <- unsynchronized read
54///   h.join().unwrap()
55/// });
56/// loaned::drop!(x);
57/// ```
58///
59/// If you need to safely send this value, you can convert it to a `LoanedMut<'t, T>` with `Into`.
60unsafe impl<'t, T: Sync> Send for Loaned<'t, T> {}
61
62impl<'t, T> Loaned<'t, T> {
63  /// Constructs a `Loaned` from a given smart pointer, returning the borrow
64  /// along with the loaned pointer.
65  #[inline]
66  pub fn loan(value: T) -> (&'t T::Target, Self)
67  where
68    T: Loanable<'t>,
69  {
70    let loaned = unsafe { Loaned::from_raw(RawLoaned::new(value)) };
71    (loaned.borrow(), loaned)
72  }
73
74  /// Creates a `Loaned` without actually loaning it. If you want to loan it,
75  /// use [`Loaned::loan`] or [`Loaned::borrow`].
76  #[inline(always)]
77  pub fn new(value: T) -> Self {
78    unsafe { Loaned::from_raw(RawLoaned::new(value)) }
79  }
80
81  /// Stores the contained value into a given place. See the [`Place`] trait for
82  /// more.
83  #[inline(always)]
84  pub fn place(self, place: &'t mut impl Place<'t, T>) {
85    Place::place(self.into(), place)
86  }
87
88  /// Borrows the pointee of the value, returning a reference valid for `'t`.
89  #[inline(always)]
90  pub fn borrow(&self) -> &'t T::Target
91  where
92    T: Loanable<'t>,
93  {
94    unsafe { &*(&**self.inner.as_ref() as *const _) }
95  }
96
97  #[inline(always)]
98  pub(crate) fn into_raw(self) -> RawLoaned<T> {
99    unsafe { ptr::read(&ManuallyDrop::new(self).inner) }
100  }
101
102  #[inline(always)]
103  pub(crate) unsafe fn from_raw(inner: RawLoaned<T>) -> Self {
104    Loaned {
105      inner,
106      _contravariant: PhantomData,
107    }
108  }
109}
110
111impl<'t, T> Deref for Loaned<'t, T> {
112  type Target = T;
113  #[inline(always)]
114  fn deref(&self) -> &T {
115    unsafe { self.inner.as_ref() }
116  }
117}
118
119impl<'t, T> Drop for Loaned<'t, T> {
120  #[cold]
121  fn drop(&mut self) {
122    #[cfg(feature = "std")]
123    if mem::needs_drop::<T>() && !std::thread::panicking() {
124      panic!(
125        "memory leak: cannot drop `{Self}`
126    if leaking is desired, use `ManuallyDrop<{Self}>` or `mem::forget`
127    otherwise, use `drop!(loaned)` to drop the inner value",
128        Self = core::any::type_name::<Self>()
129      )
130    }
131  }
132}
133
134impl<'t, T: Debug> Debug for Loaned<'t, T> {
135  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
136    f.debug_tuple("Loaned").field(&**self).finish()
137  }
138}
139
140impl<'t, T: Display> Display for Loaned<'t, T> {
141  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
142    (**self).fmt(f)
143  }
144}
145
146impl<'t, T: Clone> Clone for Loaned<'t, T> {
147  fn clone(&self) -> Self {
148    Loaned::new((**self).clone())
149  }
150}
151
152impl<'t, T: Default> Default for Loaned<'t, T> {
153  fn default() -> Self {
154    Loaned::new(Default::default())
155  }
156}
157
158impl<'t, 'u, T: PartialEq<U>, U> PartialEq<Loaned<'u, U>> for Loaned<'t, T> {
159  fn eq(&self, other: &Loaned<'u, U>) -> bool {
160    (&**self) == (&**other)
161  }
162}
163
164impl<'t, T: Eq> Eq for Loaned<'t, T> {}
165
166impl<'t, 'u, T: PartialOrd<U>, U> PartialOrd<Loaned<'u, U>> for Loaned<'t, T> {
167  fn partial_cmp(&self, other: &Loaned<'u, U>) -> Option<core::cmp::Ordering> {
168    (**self).partial_cmp(&**other)
169  }
170}
171
172impl<'t, T: Ord> Ord for Loaned<'t, T> {
173  fn cmp(&self, other: &Self) -> core::cmp::Ordering {
174    (**self).cmp(&**other)
175  }
176}
177
178impl<'t, T: Hash> Hash for Loaned<'t, T> {
179  fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
180    (**self).hash(state);
181  }
182}
183
184impl<'t, T> From<T> for Loaned<'t, T> {
185  fn from(value: T) -> Self {
186    Loaned::new(value)
187  }
188}
189
190impl<'t, T> Loaned<'t, T> {
191  /// Merges multiple `LoanedMut` values.
192  ///
193  /// # Example
194  /// ```
195  /// use loaned::Loaned;
196  /// let a = Loaned::new(1);
197  /// let b = Loaned::new(2);
198  /// let ab: Loaned<(u32, u32)> = Loaned::merge(Default::default(), |ab, m| {
199  ///   m.place(a, &mut ab.0);
200  ///   m.place(b, &mut ab.1);
201  /// });
202  /// ```
203  pub fn merge(value: T, f: impl for<'i> FnOnce(&'i mut T, &'i Merge<'t, 'i>)) -> Self {
204    unsafe {
205      let mut inner = RawLoaned::new(value);
206      f(inner.as_mut(), &Merge(PhantomData));
207      Loaned::from_raw(inner)
208    }
209  }
210}
211
212/// See [`Loaned::merge`].
213#[doc(hidden)]
214pub struct Merge<'t, 'i>(PhantomData<(&'t mut &'t (), &'i mut &'i ())>);
215
216impl<'t, 'i> Merge<'t, 'i> {
217  /// See [`Loaned::merge`].
218  pub fn place<T>(&'i self, loaned: Loaned<'t, T>, place: &'i mut impl Place<'i, T>) {
219    Place::place(unsafe { LoanedMut::from_raw(loaned.into_raw()) }, place)
220  }
221}
222
223impl<'t, T> Loaned<'t, T> {
224  /// Creates a `Loaned` with multiple sub-loans.
225  ///
226  /// # Example
227  /// ```
228  /// use loaned::Loaned;
229  /// let ((a, b), ab) = Loaned::loan_with((Box::new(1), Box::new(2)), |ab, l| {
230  ///   (l.loan(&ab.0), l.loan(&ab.1))
231  /// });
232  /// assert_eq!(*a, 1);
233  /// assert_eq!(*b, 2);
234  /// assert_eq!(loaned::take!(ab), (Box::new(1), Box::new(2)));
235  /// ```
236  pub fn loan_with<L>(
237    value: T,
238    f: impl for<'i> FnOnce(&'i mut T, &'i LoanWith<'t, 'i>) -> L,
239  ) -> (L, Self) {
240    unsafe {
241      let mut inner = RawLoaned::new(value);
242      let loans = f(inner.as_mut(), &LoanWith(PhantomData));
243      (loans, Loaned::from_raw(inner))
244    }
245  }
246}
247
248/// See [`Loaned::loan_with`].
249#[doc(hidden)]
250pub struct LoanWith<'t, 'i>(PhantomData<(&'t mut &'t (), &'i mut &'i ())>);
251
252impl<'t, 'i> LoanWith<'t, 'i> {
253  /// See [`Loaned::loan_with`].
254  pub fn loan<T: Loanable<'i>>(&'i self, value: &'i T) -> &'t T::Target {
255    unsafe { &*(&**value as *const _) }
256  }
257}