Keep Calm (and call Clone)

Build Status docs.rs crates.io

Simple shared types for multi-threaded Rust programs.

This library simplifies a number of shared-object patterns that are used in multi-threaded programs such as web-servers.

Advantages of keepcalm:

Container types

The following container types are available:

| Container | Equivalent | Notes | |--------------------------------|-----------------------|-------| | SharedMut (RwLock) | Arc<RwLock<T>> | This is the default shared-mutable type. | SharedMut (Mutex) | Arc<Mutex<T>> | In some cases it may be necessary to serialize both read and writes. For example, with types that are not Sync. | SharedMut (read/copy/update) | Arc<RwLock<Arc<T> | When the write lock of an RCU container is dropped, the values written are committed to the value in the container. | Shared (default) | Arc | This is the default shared-immutable type. Note that this is slightly more verbose: [Shared] does not [std::ops::Deref] to the underlying type and requires calling [Shared::read]. | Shared ([Shared::new_unsync])| Arc<Mutex<T>> | For types that are not Sync, a Mutex is used to serialize read-only access. | Shared ([SharedMut::shared]) | n/a | This provides a read-only view into a read-write container and has no direct equivalent.

Basic syntax

The traditional Rust shared object patterns tend to be somewhat verbose, for example:

```rust

use std::sync::{Arc, Mutex};

fn use_string(s: &str) {}

struct Foo { mystring: Arc>, myinteger: Arc>, } let foo = Foo { mystring: Arc::new(Mutex::new("123".tostring())), myinteger: Arc::new(Mutex::new(1)), }; usestring(&*foo.my_string.lock().expect("Mutex was poisoned")); ```

We can reduce a some of the ceremony and verbosity with keepcalm:

```rust

use keepcalm::*;

fn use_string(s: &str) {}

struct Foo { mystring: SharedMut, myinteger: SharedMut, } let foo = Foo { mystring: SharedMut::new("123".tostring()), myinteger: SharedMut::new(1), }; usestring(&*foo.my_string.read()); ```

SharedMut

The [SharedMut] object hides the complexity of managing Arc<Mutex<T>>, Arc<RwLock<T>>, and other synchronization types behind a single interface:

```rust

use keepcalm::*;

let object = "123".to_string(); let shared = SharedMut::new(object); shared.read(); ```

By default, a [SharedMut] object uses Arc<RwLock<T>> under the hood, but you can choose the synchronization primitive at construction time. The [SharedMut] object erases the underlying primitive and you can use them interchangeably:

```rust

use keepcalm::*;

fn use_shared(shared: SharedMut) { shared.read(); }

let shared = SharedMut::new("123".tostring()); useshared(shared); let shared = SharedMut::newwithtype("123".tostring(), Implementation::Mutex); useshared(shared); ```

Managing the poison state of synchronization primitives can be challenging as well. Rust will poison a Mutex or RwLock if you hold a lock while a panic! occurs.

The SharedMut type allows you to specify a [PoisonPolicy] at construction time. By default, if a synchronization primitive is poisoned, the SharedMut will panic! on access. This can be configured so that poisoning is ignored:

```rust

use keepcalm::*;

let shared = SharedMut::newwithpolicy("123".to_string(), PoisonPolicy::Ignore); ```

Shared

The [Shared] object is similar to Rust's [std::sync::Arc], but adds the ability to project.

Projection

Both [Shared] and [SharedMut] allow projection into the underlying type. Projection can be used to select either a subset of a type, or to cast a type to a trait.

Note that projections are always linked to the root object!

Casting:

```rust

use keepcalm::*;

let shared = SharedMut::new("123".tostring()); let sharedasref: SharedMut> = shared.project(project_cast!(x: String => dyn AsRef)); ```

Subset of a struct/tuple:

```rust

use keepcalm::*;

[derive(Default)]

struct Foo { tuple: (String, usize) }

let shared = SharedMut::new(Foo::default()); let shared_string: SharedMut = shared.project(project!(x: Foo, x.tuple.0));

shared_string.write() += "hello, world"; assert_eq!(shared.read().tuple.0, "hello, world"); assert_eq!(shared_string.read(), "hello, world"); ```

Unsized types

Both [Shared] and [SharedMut] support unsized types, but due to current limitations in the language (see [std::ops::CoerceUnsized] for details), you need to construct them in special ways.

Unsized traits are supported, but you will either need to specify Send + Sync in the shared type, or [project_cast!] the object:

```rust

use keepcalm::*;

// In this form, Send + Sync are visible in the shared type let boxed: Box + Send + Sync> = Box::new("123".tostring()); let shared: SharedMut + Send + Sync> = SharedMut::frombox(boxed);

// In this form, Send + Sync are erased via projection let shared = SharedMut::new("123".tostring()); let sharedasref: SharedMut> = shared.project(project_cast!(x: String => dyn AsRef)); ```

Unsized slices are supported using a box:

```rust

use keepcalm::*;

let boxed: Box<[i32]> = Box::new([1, 2, 3]); let shared: SharedMut<[i32]> = SharedMut::from_box(boxed); ```