deref_owned 0.10.2

Generalization of std::borrow::Cow
Documentation
//! Generalization of [`std::borrow::Cow`]
//!
//! `deref_owned` provides a wrapper [`Owned<B>`](Owned) which stores an inner
//! value of type [`<B as ToOwned>::Owned`](ToOwned::Owned) and points to a
//! value of type `B` by implementing [`Deref<Target = B>`](Deref) through
//! [`Borrow::borrow`].
//!
//! `Owned` is similar to [`Cow`], except that it's always owning a value. This
//! can be used to avoid runtime overhead in certain scenarios.
//!
//! Moreover, a trait [`GenericCow`] is provided, which allows conversion from
//! certain types into an owned type through an
//! [`.into_owned()`](GenericCow::into_owned) method. The trait
//! `GenericCow<Borrowed = B>` is a generalization of `Cow<'_, B>` and is
//! implemented for:
//!
//! * [`&B`](prim@reference)
//! * [`Cow<'_, B>`](Cow)
//! * [`Owned<B>`](Owned)
//!
//! # Example
//!
//! ```
//! use deref_owned::{GenericCow, Owned};
//! use std::borrow::{Borrow, Cow};
//!
//! fn generic_fn(arg: impl GenericCow<Borrowed = str>) {
//!     let reference: &str = &*arg; // or: arg.borrow()
//!     assert_eq!(reference, "Hello");
//!     let owned: String = arg.into_owned();
//!     assert_eq!(owned, "Hello".to_string());
//! }
//!
//! generic_fn(Owned("Hello".to_string()));
//! generic_fn(Cow::Owned("Hello".to_string()));
//! generic_fn(Cow::Borrowed("Hello"));
//! generic_fn("Hello");
//! ```

#![warn(missing_docs)]

use std::borrow::{Borrow, Cow};
use std::cmp;
use std::fmt;
use std::hash;
use std::ops::Deref;

/// Wrapper akin to [`Cow::Owned`] but known to be owned at compile-time
///
/// The wrapper takes a type argument `B` and a value of type
/// `<B as ToOwned>::Owned`.
/// It supports dereference (with `B` being the [target](Deref::Target)),
/// implements [`Borrow<B>`](Borrow), and
/// [`GenericCow<Borrowed = B>`](GenericCow).
///
/// # Example
///
/// ```
/// use deref_owned::{GenericCow, Owned};
/// use std::borrow::Cow;
///
/// fn generic_fn(arg: impl GenericCow<Borrowed = str>) { /* ... */ }
///
/// generic_fn(Owned("Hello".to_string()));
/// generic_fn(Cow::Owned("Hello".to_string()));
/// generic_fn(Cow::Borrowed("Hello"));
/// generic_fn("Hello");
/// ```
#[repr(transparent)]
pub struct Owned<B>(pub <B as ToOwned>::Owned)
where
    B: ?Sized + ToOwned;

impl<B> Clone for Owned<B>
where
    B: ?Sized + ToOwned,
    <B as ToOwned>::Owned: Clone,
{
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
    fn clone_from(&mut self, source: &Self) {
        self.0 = source.0.clone();
    }
}

impl<B> Default for Owned<B>
where
    B: ?Sized + ToOwned,
    <B as ToOwned>::Owned: Default,
{
    fn default() -> Self {
        Self(Default::default())
    }
}

impl<B, C> PartialEq<Owned<C>> for Owned<B>
where
    B: ?Sized + ToOwned,
    C: ?Sized + ToOwned,
    <B as ToOwned>::Owned: PartialEq<<C as ToOwned>::Owned>,
{
    fn eq(&self, other: &Owned<C>) -> bool {
        self.0.eq(&other.0)
    }
    fn ne(&self, other: &Owned<C>) -> bool {
        self.0.ne(&other.0)
    }
}

impl<B> Eq for Owned<B>
where
    B: ?Sized + ToOwned,
    <B as ToOwned>::Owned: Eq,
{
}

impl<B, C> cmp::PartialOrd<Owned<C>> for Owned<B>
where
    B: ?Sized + ToOwned,
    C: ?Sized + ToOwned,
    <B as ToOwned>::Owned: cmp::PartialOrd<<C as ToOwned>::Owned>,
{
    fn partial_cmp(&self, other: &Owned<C>) -> Option<cmp::Ordering> {
        self.0.partial_cmp(&other.0)
    }
    fn lt(&self, other: &Owned<C>) -> bool {
        self.0.lt(&other.0)
    }
    fn le(&self, other: &Owned<C>) -> bool {
        self.0.le(&other.0)
    }
    fn gt(&self, other: &Owned<C>) -> bool {
        self.0.gt(&other.0)
    }
    fn ge(&self, other: &Owned<C>) -> bool {
        self.0.ge(&other.0)
    }
}

impl<B> cmp::Ord for Owned<B>
where
    B: ?Sized + ToOwned,
    <B as ToOwned>::Owned: cmp::Ord,
{
    fn cmp(&self, other: &Owned<B>) -> cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl<B> hash::Hash for Owned<B>
where
    B: ?Sized + ToOwned,
    <B as ToOwned>::Owned: hash::Hash,
{
    fn hash<H>(&self, state: &mut H)
    where
        H: hash::Hasher,
    {
        self.0.hash(state)
    }
}

impl<B> Deref for Owned<B>
where
    B: ?Sized + ToOwned,
{
    type Target = B;
    fn deref(&self) -> &B {
        self.0.borrow()
    }
}

impl<B> Borrow<B> for Owned<B>
where
    B: ?Sized + ToOwned,
{
    fn borrow(&self) -> &B {
        self.0.borrow()
    }
}

/// [`AsRef`] is only implemented when the [inner type](Owned::0) implements
/// `AsRef`. This is consistent with the blanket implementation of `AsRef` for
/// [shared references](prim@reference) but not consistent with [`Cow`]'s
/// implementation of `AsRef`.
impl<B, U> AsRef<U> for Owned<B>
where
    B: ?Sized + ToOwned,
    <B as ToOwned>::Owned: AsRef<U>,
    U: ?Sized,
{
    fn as_ref(&self) -> &U {
        self.0.as_ref()
    }
}

impl<B: fmt::Display> fmt::Display for Owned<B>
where
    B: ?Sized + ToOwned,
    <B as ToOwned>::Owned: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl<B: fmt::Debug> fmt::Debug for Owned<B>
where
    B: ?Sized + ToOwned,
    <B as ToOwned>::Owned: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(&self.0, f)
    }
}

/// Generalized [`Cow`]
///
/// The trait `GenericCow<Borrowed = B>` is a generalization of `Cow<'_, B>`
/// and is implemented for:
///
/// * [`&B`](prim@reference)
/// * [`Cow<'_, B>`](Cow)
/// * [`Owned<B>`](Owned)
///
/// # Example
///
/// ```
/// use deref_owned::GenericCow;
/// use std::borrow::Borrow;
///
/// fn generic_fn(arg: impl GenericCow<Borrowed = str>) {
///     let reference: &str = &*arg; // or: arg.borrow()
///     assert_eq!(reference, "Hello");
///     let owned: String = arg.into_owned();
///     assert_eq!(owned, "Hello".to_string());
/// }
/// ```
///
/// # Design considerations
///
/// Note that the trait definition contains some redundancy. Either
///
/// * the associated type [`GenericCow::Borrowed`] or
/// * the [`Deref`] supertrait
///
/// could have been omitted. Both have been included to simplify syntax when
/// using the trait:
///
/// * Including the associated type allows specifying the borrowed type in
///   bounds easily without having to add an extra `Deref` bound (which would
///   require `std::ops::Deref` being in scope).
/// * Using `Deref` as supertrait ensures that it's easy to go to the borrowed
///   type using the operator combination "`&*`" instead of having to use the
///   horrible syntax
///   "`Borrow::<<Type as GenericCow>::Borrowed>::borrow(&value)`" when type
///   inference would fail when simply using `.borrow()`. Moreover, deref
///   coercion improves ergonomics.
pub trait GenericCow
where
    Self: Sized,
    Self: Borrow<Self::Borrowed>,
    Self: Deref<Target = Self::Borrowed>,
{
    /// Borrowed type
    ///
    /// This associated type is identical to
    /// [`<Self as Deref>::Target`](Deref::Target), which causes some
    /// redundancy when implementing the trait but simplifies syntax when using
    /// the trait ([`Deref`] doesn't need to be in scope to specify the
    /// borrowed type).
    type Borrowed: ?Sized + ToOwned;
    /// Convert into owned type
    ///
    /// Opposed to [`ToOwned::to_owned`], this method consumes the receiver,
    /// which allows avoiding unnecessary clones in some cases. I.e. using this
    /// method on an [`Owned`] value (or on a [`Cow::Owned`] enum variant) will
    /// simply unwrap it. In case of using the `Owned` struct of this crate
    /// (instead of `Cow`), this operation is zero-cost.
    fn into_owned(self) -> <Self::Borrowed as ToOwned>::Owned;
}

impl<'a, B> GenericCow for &'a B
where
    B: ?Sized + ToOwned,
{
    type Borrowed = B;
    fn into_owned(self) -> <B as ToOwned>::Owned {
        self.to_owned()
    }
}

impl<'a, B> GenericCow for Cow<'a, B>
where
    B: ?Sized + ToOwned,
{
    type Borrowed = B;
    fn into_owned(self) -> <B as ToOwned>::Owned {
        Cow::into_owned(self)
    }
}

impl<B> GenericCow for Owned<B>
where
    B: ?Sized + ToOwned,
{
    type Borrowed = B;
    fn into_owned(self) -> <B as ToOwned>::Owned {
        self.0
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::borrow::Cow;
    #[test]
    fn test_owned() {
        let wrapped: Owned<str> = Owned("Alpha".to_string());
        let reference: &str = &*wrapped;
        assert_eq!(reference, "Alpha");
        let owned: String = wrapped.into_owned();
        assert_eq!(owned, "Alpha".to_string());
    }
    #[test]
    fn test_cow() {
        let cow_owned: Cow<'static, str> = Cow::Owned("Bravo".to_string());
        assert_eq!(GenericCow::into_owned(cow_owned), "Bravo".to_string());
        let cow_borrowed: Cow<'static, str> = Cow::Borrowed("Charlie");
        assert_eq!(GenericCow::into_owned(cow_borrowed), "Charlie".to_string());
    }
    #[test]
    fn test_ref() {
        let reference: &str = "Delta";
        assert_eq!(GenericCow::into_owned(reference), "Delta".to_string());
    }
    #[test]
    fn test_vec() {
        let wrapped: Owned<[i32]> = Owned(vec![1, 2, 3]);
        assert_eq!(&*wrapped, &[1, 2, 3] as &[i32]);
        assert_eq!(wrapped.into_owned(), vec![1, 2, 3]);
    }
    #[test]
    fn test_generic_fn() {
        fn generic_fn(arg: impl GenericCow<Borrowed = str>) {
            let reference1: &str = &*arg;
            assert_eq!(reference1, "Echo");
            let reference2: &str = arg.borrow();
            assert_eq!(reference2, "Echo");
            let owned: String = arg.into_owned();
            assert_eq!(owned, "Echo".to_string());
        }
        generic_fn(Owned("Echo".to_string()));
        generic_fn(Cow::Owned("Echo".to_string()));
        generic_fn(Cow::Borrowed("Echo"));
        generic_fn("Echo");
    }
    #[test]
    fn test_vec_borrow() {
        let wrapped: Owned<[i32]> = Owned(vec![2, 7, 4]);
        let slice_ref: &[i32] = wrapped.borrow();
        assert_eq!(slice_ref, &[2, 7, 4] as &[i32]);
    }
    #[test]
    fn test_int_borrow() {
        let wrapped: Owned<i32> = Owned(5);
        let reference: &i32 = wrapped.borrow();
        assert_eq!(reference, &5);
    }
}