renttoown

Build Status

RentToOwn<T>: A wrapper type for optionally giving up ownership of the underlying value.

RentToOwn<T> is useful in situations where

  1. a function might want to conditionally take ownership of some T value, and

  2. that function cannot take the T by value and return an Option<T> to maybe give the T value back if it doesn't want ownership.

RentToOwn<T> dereferences (immutably and mutably) to its inner T value, and additionally provides a take method that gives up ownership of the inner value to the caller.

Under the covers, RentToOwn<T> is essentially an Option<T> that gets unwrapped when dereferenced and calls Option::take if we need to take ownership of the inner value. The key advantage over using Option<T> directly, other than the Deref sugar, is some lifetime trickery to statically prevent all unwrapping panics that would arise from using the RentToOwn<T> wrapper again after the inner value has been taken. Once the inner value is taken, the borrow checker will ensure that the original RentToOwn<T> cannot be used anymore. See the take method's documentation for details.

Example

In this example, if the configure function encounters any errors, we do not wish to drop the BigExpensiveResource, but instead allow the caller to handle the error and then reuse the resource. In effect, the configure function is conditionally taking ownership of the BigExpensiveResource depending on if there are IO errors or not.

```rust use renttoown::RentToOwn;

use std::io::{self, Read}; use std::fs;

/// This is a big, expensive to create (or maybe even unique) resource, and we /// want to reuse it even if configure returns an error. struct BigExpensiveResource { // ... }

[derive(Default)]

struct Config { // ... }

/// A big, expensive resource that has been properly configured. struct ConfiguredResource { resource: BigExpensiveResource, config: Config, }

fn readandparseconfigfile() -> io::Result { // ... }

fn configure<'a>( resource: &'a mut RentToOwn<'a, BigExpensiveResource> ) -> io::Result { // We use normal error propagation with ?. Because we haven't taken the // resource out of the RentToOwn, if we early return here the caller still // controls the BigExpensiveResource and it isn't dropped. let config = readandparseconfigfile()?;

// Now we `take` ownership of the resource and return the configured
// resource.
let resource = resource.take();
Ok(ConfiguredResource { resource, config })

} ```

What does configure's caller look like? It calls RentToOwn::with to construct the RentToOwn<BigExpensiveResource> and invoke a closure with it. Then it inspects the results of the closure and whether the BigExpensiveResource was taken or not.

In this example, the caller can recover from any IO error when reading or parsing the configuration file and use a default configuration with the BigExpensiveResource instead.

``rust fn use_custom_configuration_or_default(resource: BigExpensiveResource) -> ConfiguredResource { // We pass the resource intowithand it constructs theRentToOwn // wrapper around it and then gives the wrapper to the closure. Finally, it // returns a pair of anOptionwhich isSomeif // the closure took ownership andNone` if it did not, and the closure's // return value. let (resource, result) = RentToOwn::with(resource, |resource| { configure(resource) });

if let Ok(configured) = result {
    return configured;
}

// Reuse the resource if the closure did not take ownership or else
// reconstruct it if the closure did take ownership. (In this particular
// example, we know that `configure` took ownership if and only if the
// result was `Ok`, but that doesn't hold for all possible examples.)
// Finally, return the configured resource with the default configuration.
let resource = resource.unwrap_or_else(|| BigExpensiveResource::reconstruct());
let config = Config::default();
ConfiguredResource { resource, config }

} ```

License: Apache-2.0/MIT