#smart-pointers #borrowed #owned #value #ownership #internally #unifies

ctb

ctb unifies borrowed and owned values into a smart pointer that is internally borrowed to retain ownership

2 stable releases

1.1.0 Apr 25, 2024
1.0.0 Apr 23, 2024

#682 in Algorithms

Download history 115/week @ 2024-04-18 211/week @ 2024-04-25

326 downloads per month

MIT/Apache

20KB
253 lines

Introduces a type that unifies borrowed and owned values into a smart pointer that is internally borrowed to retain ownership.

Overview

This library provides the CloneOnExtract type, which is a smart pointer that can hold either a borrowed or owned value. This allows you to create an interface for the modules your libary that can work with both borrowed and owned values by delegating the complexity of ownership to simple low level concretions.

Acronym

  • CloneOnExtract - Cow To Borrowed. This is due to the similar functionality to Cow, yet is not the same as Cow and has different use cases and functions that expand on the limitations of Cow in a more useful manner.

Structure

The CloneOnExtract type is generic over the owned value and borrowed value, and can be used with any two types that follow a "borrowed and owned" heirachy.

Traits

The CloneOnExtract type also implements the Deref, DerefMut, Borrow, BorrowMut, AsRef, and AsMut traits, which allows maximum flexibility in treating the type as if it were an owned value.

This library also provides three traits, IntoCloneOnExtract, IntoDerefCloneOnExtract, and AsCloneOnExtract, which add convenient methods all types that fit the requirements of the CloneOnExtract type. These types are automatically implemented so there is no need implement them yourself.

Methods

The CloneOnExtract type also provides a number of convenient methods for working with the value it is holding, such as as_owned, as_borrowed, is_owned, and is_borrowed.

Examples

Unifying 'by value' and 'by reference' trait implementations

use CloneOnExtract::{CloneOnExtract, AsCloneOnExtract, IntoCloneOnExtract};

pub trait AddTogether<T> {
    fn add_together(self, other: T) -> i32;
}

impl AddTogether<u8> for u8 {
    fn add_together(self, other: u8) -> i32 {
        (self + other) as i32
    }
}

impl AddTogether<&i32> for i32 {
    fn add_together(self, other: &i32) -> i32 {
        self + other
    }
}

// The complication is seen in the following function, it is possible to bypass
// by cloning or implementing 2 different functions but this may not be possible
// in more complex situations that involve multiple levels of indirection.

// pub fn add_together_and_double<T: AddTogether<T / &T ???>>(lhs: T, rhs: ???)

// However, by using `CloneOnExtract`, this is possible with only a small runtime overhead
// that the compiler should optimize away if possible.

impl AddTogether<CloneOnExtract<'_, u8, u8>> for CloneOnExtract<'_, u8, u8> {
    fn add_together(self, other: CloneOnExtract<u8, u8>) -> i32 {
        self.into_owned().add_together(other.into_owned())
    }
}

impl AddTogether<CloneOnExtract<'_, i32, i32>> for CloneOnExtract<'_, i32, i32> {
    fn add_together(self, other: CloneOnExtract<i32, i32>) -> i32 {
        self.into_owned().add_together(other.as_ref())
    }
}

// Despite the complexity of the bounds for this function, in much more complex
// use cases it can dramatically decreases complexity and redundancy. The main
// point though, is that the function is extremely simple to call and can easily
// be composed in further functions using `CloneOnExtract` and is completely generic over
// value or reference arguments.
pub fn add_together_and_double<T: Clone>(lhs: CloneOnExtract<T, T>, rhs: CloneOnExtract<T, T>) -> i32
where
    for<'a, 'b> CloneOnExtract<'a, T, T>: AddTogether<CloneOnExtract<'b, T, T>>,
{
    lhs.as_borrowed().add_together(rhs.as_borrowed()) + lhs.as_borrowed().add_together(rhs)
}

// Note: CloneOnExtract() converts to an owned value but if we only had a reference we could
// use as_CloneOnExtract() insead. If our type implemented Deref then we also could use
// CloneOnExtract_deref() instead of cbt().
fn test() {
    assert_eq!(add_together_and_double(1u8.CloneOnExtract(), 2u8.CloneOnExtract()), 6);
    assert_eq!(add_together_and_double(1i32.CloneOnExtract(), 2i32.CloneOnExtract()), 6);
}

No runtime deps