Code Monkey home page Code Monkey logo

rent_to_own's Introduction

rent_to_own

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.

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 `None` if
    // the closure took ownership and `Some` 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

rent_to_own's People

Contributors

burdges avatar burntpizza avatar fitzgen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.