2 stable releases
1.1.0 | Apr 25, 2024 |
---|---|
1.0.0 | Apr 23, 2024 |
#3 in #borrowed
22 downloads per month
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 -
C
owT
oB
orrowed. This is due to the similar functionality toCow
, yet is not the same asCow
and has different use cases and functions that expand on the limitations ofCow
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);
}