rent_to_own/
lib.rs

1/*!
2
3[![](https://docs.rs/rent_to_own/badge.svg)](https://docs.rs/rent_to_own/) [![](https://img.shields.io/crates/v/rent_to_own.svg)](https://crates.io/crates/rent_to_own) [![](https://img.shields.io/crates/d/rent_to_own.png)](https://crates.io/crates/rent_to_own) [![Build Status](https://travis-ci.org/fitzgen/rent_to_own.png?branch=master)](https://travis-ci.org/fitzgen/rent_to_own)
4
5`RentToOwn<T>`: A wrapper type for optionally giving up ownership of the
6underlying value.
7
8`RentToOwn<T>` is useful in situations where
9
101. a function might want to *conditionally take ownership* of some `T`
11value, and
12
132. that function cannot take the `T` by value and return an `Option<T>` to maybe
14give the `T` value back if it doesn't want ownership.
15
16`RentToOwn<T>` dereferences (immutably and mutably) to its inner `T` value, and
17additionally provides a `take` method that gives up ownership of the inner value
18to the caller.
19
20Under the covers, `RentToOwn<T>` is essentially an `Option<T>` that gets
21unwrapped when dereferenced and calls `Option::take` if we need to take
22ownership of the inner value. The key advantage over using `Option<T>` directly,
23other than the `Deref` sugar, is some lifetime trickery to statically prevent
24all unwrapping panics that would arise from using the `RentToOwn<T>` wrapper
25again after the inner value has been taken. Once the inner value is taken, the
26borrow checker will ensure that the original `RentToOwn<T>` cannot be used
27anymore. See the `take` method's documentation for details.
28
29## Example
30
31In this example, if the `configure` function encounters any errors, we do not
32wish to drop the `BigExpensiveResource`, but instead allow the caller to handle
33the error and then reuse the resource. In effect, the `configure` function is
34conditionally taking ownership of the `BigExpensiveResource` depending on if
35there are IO errors or not.
36
37```
38use rent_to_own::RentToOwn;
39
40use std::io::{self, Read};
41use std::fs;
42
43/// This is a big, expensive to create (or maybe even unique) resource, and we
44/// want to reuse it even if `configure` returns an error.
45struct BigExpensiveResource {
46    // ...
47}
48
49#[derive(Default)]
50struct Config {
51    // ...
52}
53
54/// A big, expensive resource that has been properly configured.
55struct ConfiguredResource {
56    resource: BigExpensiveResource,
57    config: Config,
58}
59
60fn read_and_parse_config_file() -> io::Result<Config> {
61    // ...
62#   Ok(Config {})
63}
64
65fn configure<'a>(
66    resource: &'a mut RentToOwn<'a, BigExpensiveResource>
67) -> io::Result<ConfiguredResource> {
68    // We use normal error propagation with `?`. Because we haven't `take`n the
69    // resource out of the `RentToOwn`, if we early return here the caller still
70    // controls the `BigExpensiveResource` and it isn't dropped.
71    let config = read_and_parse_config_file()?;
72
73    // Now we `take` ownership of the resource and return the configured
74    // resource.
75    let resource = resource.take();
76    Ok(ConfiguredResource { resource, config })
77}
78```
79
80What does `configure`'s caller look like? It calls `RentToOwn::with` to
81construct the `RentToOwn<BigExpensiveResource>` and invoke a closure with
82it. Then it inspects the results of the closure and whether the
83`BigExpensiveResource` was taken or not.
84
85In this example, the caller can recover from any IO error when reading or
86parsing the configuration file and use a default configuration with the
87`BigExpensiveResource` instead.
88
89```
90# use rent_to_own::RentToOwn;
91# struct BigExpensiveResource;
92# impl BigExpensiveResource { fn reconstruct() -> Self { BigExpensiveResource } }
93# #[derive(Default)]
94# struct Config;
95# struct ConfiguredResource {
96#     resource: BigExpensiveResource,
97#     config: Config,
98# }
99# fn configure<'a>(
100#     resource: &'a mut RentToOwn<'a, BigExpensiveResource>
101# ) -> ::std::io::Result<ConfiguredResource> {
102#     unimplemented!()
103# }
104fn use_custom_configuration_or_default(resource: BigExpensiveResource) -> ConfiguredResource {
105    // We pass the resource into `with` and it constructs the `RentToOwn`
106    // wrapper around it and then gives the wrapper to the closure. Finally, it
107    // returns a pair of an `Option<BigExpensiveResource>` which is `Some` if
108    // the closure took ownership and `None` if it did not, and the closure's
109    // return value.
110    let (resource, result) = RentToOwn::with(resource, |resource| {
111        configure(resource)
112    });
113
114    if let Ok(configured) = result {
115        return configured;
116    }
117
118    // Reuse the resource if the closure did not take ownership or else
119    // reconstruct it if the closure did take ownership. (In this particular
120    // example, we know that `configure` took ownership if and only if the
121    // result was `Ok`, but that doesn't hold for all possible examples.)
122    // Finally, return the configured resource with the default configuration.
123    let resource = resource.unwrap_or_else(|| BigExpensiveResource::reconstruct());
124    let config = Config::default();
125    ConfiguredResource { resource, config }
126}
127```
128
129 */
130
131#![deny(missing_docs)]
132#![deny(missing_debug_implementations)]
133
134use std::ops::{Deref, DerefMut};
135
136/// A wrapper around a `T` that allows users to conditionally take ownership of
137/// the inner `T` value, or simply use it like a `&mut T` reference.
138///
139/// See the module documentation for details and examples.
140#[derive(Debug, Hash)]
141pub struct RentToOwn<'a, T: 'a> {
142    inner: &'a mut Option<T>,
143}
144
145impl<'a, T> Deref for RentToOwn<'a, T> {
146    type Target = T;
147    fn deref(&self) -> &T {
148        self.inner.as_ref().unwrap()
149    }
150}
151
152impl<'a, T> DerefMut for RentToOwn<'a, T> {
153    fn deref_mut(&mut self) -> &mut T {
154        self.inner.as_mut().unwrap()
155    }
156}
157
158impl<'a, T: 'a> RentToOwn<'a, T> {
159    /// Give the function `f` the option to take ownership of `inner`.
160    ///
161    /// That is, create a `RentToOwn` from the given `inner` value and then
162    /// invoke the function `f` with it.
163    ///
164    /// The return value is a pair of:
165    ///
166    /// 1. If the closure took ownership of the inner value, `None`, otherwise
167    /// `Some(inner)`.
168    ///
169    /// 2. The value returned by the closure.
170    ///
171    /// See the module level documentation for details and examples.
172    pub fn with<F, U>(inner: T, f: F) -> (Option<T>, U)
173    where
174        F: for<'b> FnOnce(&'b mut RentToOwn<'b, T>) -> U,
175    {
176        let mut inner = Some(inner);
177        let u = {
178            let mut me = RentToOwn { inner: &mut inner };
179            f(&mut me)
180        };
181        (inner, u)
182    }
183}
184
185impl<'a, T> RentToOwn<'a, T> {
186    /// Take ownership of the inner `T` value.
187    ///
188    /// Note that the lifetime on the `self` reference forces the mutable borrow
189    /// to last for the rest of the `RentToOwn`'s existence. This "tricks" the
190    /// borrow checker into statically disallowing use-after-take, which would
191    /// otherwise result in a panic if you were using `Option<T>` instead of
192    /// `RentToOwn<T>`.
193    ///
194    /// ```compile_fail
195    /// use rent_to_own::RentToOwn;
196    ///
197    /// struct Thing(usize);
198    ///
199    /// fn use_after_take<'a>(outer: &'a mut RentToOwn<'a, Thing>) {
200    ///     // Take ownership of the inner value, moving it out of the
201    ///     // `RentToOwn`.
202    ///     let inner = outer.take();
203    ///
204    ///     let inner_val = inner.0;
205    ///     println!("inner's value is {}", inner_val);
206    ///
207    ///     // An attempt to use the `RentToOwn` again (via deref) after its
208    ///     // value has already been taken!
209    ///     let outer_val = outer.0;
210    ///     println!("outer's value is {}", outer_val);
211    /// }
212    /// ```
213    ///
214    /// Attempting to compile that example results in a compilation error:
215    ///
216    /// ```text
217    ///	error[E0502]: cannot borrow `*outer` as immutable because it is also borrowed as mutable
218    ///    --> src/lib.rs:18:21
219    ///    |
220    /// 11 |     let inner = outer.take();
221    ///    |                 ----- mutable borrow occurs here
222    /// ...
223    /// 18 |     let outer_val = outer.0;
224    ///    |                     ^^^^^ immutable borrow occurs here
225    /// 19 |     println!("outer's value is {}", outer_val);
226    /// 20 | }
227    ///    | - mutable borrow ends here
228    /// ```
229    pub fn take(&'a mut self) -> T {
230        self.inner.take().unwrap()
231    }
232}
233
234#[cfg(test)]
235mod tests {
236    use super::RentToOwn;
237
238    #[test]
239    fn it_derefs() {
240        RentToOwn::with(5, |x| {
241            assert_eq!(**x, 5);
242        });
243    }
244
245    #[test]
246    fn it_derefs_mut() {
247        RentToOwn::with(5, |x| {
248            **x = 6;
249            assert_eq!(**x, 6);
250        });
251    }
252
253    #[test]
254    fn it_takes() {
255        RentToOwn::with(5, |x| {
256            assert_eq!(x.take(), 5);
257        });
258    }
259
260    #[test]
261    fn with_returns_closures_result() {
262        let (_, x) = RentToOwn::with(5, |_| 9);
263        assert_eq!(x, 9);
264    }
265
266    #[test]
267    fn with_gives_back_untaken_ownership() {
268        let (orig, _) = RentToOwn::with(5, |_| {});
269        assert_eq!(orig, Some(5));
270    }
271
272    #[test]
273    fn with_does_not_give_back_taken_ownership() {
274        let (orig, _) = RentToOwn::with(5, |x| x.take());
275        assert!(orig.is_none());
276    }
277}