2 unstable releases

0.2.0 Jan 28, 2024
0.1.0 Jan 25, 2024

#631 in Rust patterns

Download history 1/week @ 2024-02-13 14/week @ 2024-02-20 19/week @ 2024-02-27 2/week @ 2024-03-26 24/week @ 2024-04-02 30/week @ 2024-04-16

56 downloads per month

MIT license

9KB
90 lines

Tupperware

crate version docs

Crate that lets you decide if you want to put your types into a Box. It allows you to express polymorphism over how your data is stored.

Explanation

Sometimes you have a struct, in which you would like to store the data differently depending on circumstance. Some examples are:

  • You want to be able to switch between using a Rc and an Arc, depending on the presence of a feature that enables support for multithreading (while paying the cost of using an atomic reference counter).
  • You want to be able to store references to data in circumstances.

Tupperware can help here, by allowing you to use the type system to switch between different data storage variations. You can write your struct definitions semantially, capturing what data each field should hold, and then switch between different storage representations of your data.

Usage

For example, you can define a struct such as this:

use tupperware::{Storage, Inline};

type OrderId = i64;

#[derive(Debug, Clone)]
struct MyData<S: Storage = Inline> {
    name: S::Value<str>,
    orders: S::Value<[OrderId]>,
}

Now, depending on the Storage type parameter, your values will be stored differently. Here is how the struct would be stored for each of the default storage strategies:

Inline
#[derive(Debug, Clone)]
struct MyData {
    name: String,
    orders: Vec<OrderId>,
}
Box
#[derive(Debug, Clone)]
struct MyData {
    name: Box<str>,
    orders: Box<[OrderId]>,
}
Arc
#[derive(Debug, Clone)]
struct MyData {
    name: Arc<str>,
    orders: Arc<[OrderId]>,
}
Rc
#[derive(Debug, Clone)]
struct MyData {
    name: Rc<str>,
    orders: Rc<[OrderId]>,
}
Ref<'a>
#[derive(Debug, Clone)]
struct MyData {
    name: &'a str,
    orders: &'a [OrderId]>,
}

This example shows how the storage strategy now lets you control how your type is stored. It allows you to specialize the storage mechanism easily, for example swapping out Rc for Arc if a given feature is enabled:

#[cfg(feature = "sync")]
pub type MyData = types::MyData<Arc>;
#[cfg(not(feature = "sync"))]
pub type MyData = types::MyData<Rc>;

With this code, you can now use the type parameter to control how your types are stored. This is set to default to storing the type inline, however you have a couple of built-in strategies. Here is an example of how str maps using different strategies:

You can also define your own storage mechanisms by implementing the Storage trait.

License

MIT, see LICENSE.md.

No runtime deps