rent_to_own/lib.rs
1/*!
2
3[](https://docs.rs/rent_to_own/) [](https://crates.io/crates/rent_to_own) [](https://crates.io/crates/rent_to_own) [](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}