Crates.io | rent_to_own |
lib.rs | rent_to_own |
version | 0.1.0 |
source | src |
created_at | 2017-11-29 23:34:46.757731 |
updated_at | 2017-11-29 23:34:46.757731 |
description | A wrapper type for optionally giving up ownership of the underlying value. |
homepage | |
repository | https://github.com/fitzgen/rent_to_own |
max_upload_size | |
id | 41034 |
size | 27,279 |
RentToOwn<T>
: A wrapper type for optionally giving up ownership of the
underlying value.
RentToOwn<T>
is useful in situations where
a function might want to conditionally take ownership of some T
value, and
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.
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.
use rent_to_own::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 read_and_parse_config_file() -> io::Result<Config> {
// ...
}
fn configure<'a>(
resource: &'a mut RentToOwn<'a, BigExpensiveResource>
) -> io::Result<ConfiguredResource> {
// We use normal error propagation with `?`. Because we haven't `take`n the
// resource out of the `RentToOwn`, if we early return here the caller still
// controls the `BigExpensiveResource` and it isn't dropped.
let config = read_and_parse_config_file()?;
// 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.
fn use_custom_configuration_or_default(resource: BigExpensiveResource) -> ConfiguredResource {
// We pass the resource into `with` and it constructs the `RentToOwn`
// wrapper around it and then gives the wrapper to the closure. Finally, it
// returns a pair of an `Option<BigExpensiveResource>` which is `Some` if
// the closure took ownership and `None` 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