blob: 800f1bdb94bb5fc2138a1688f9a0c2817f61c4a5 [file] [log] [blame]
//! This crate only provides the `MaybeOwned` enum
//!
//! Take a look at it's documentation for more information.
//!
#![warn(missing_docs)]
#[cfg(feature = "serde")]
extern crate serde;
#[cfg(feature = "serde")]
mod serde_impls;
mod transitive_impl;
use std::ops::Deref;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::fmt;
use std::borrow::{Cow, Borrow};
use std::str::FromStr;
use self::MaybeOwned::*;
/// This type provides a way to store data to which you either have a
/// reference to or which you do own.
///
/// It provides `From<T>`, `From<&'a T>` implementations and, in difference
/// to `Cow` does _not_ require `ToOwned` to be implemented which makes it
/// compatible with non cloneable data, as a draw back of this it does not
/// know about `ToOwned`. As a consequence of it can't know that `&str` should
/// be the borrowed version of `String` and not `&String` this is especially
/// bad wrt. `Box` as the borrowed version of `Box<T>` would be `&Box<T>`.
///
/// While this crate has some drawbacks compared to `Cow` is has the benefit,
/// that it works with Types which neither implement `Clone` nor `ToOwned`.
/// Another benefit lies in the ability to write API functions which accept
/// a generic parameter `E: Into<MaybeOwned<'a, T>>` as the API consumer can
/// pass `T`, `&'a T` and `MaybeOwned<'a, T>` as argument, without requiring
/// a explicit `Cow::Onwed` or a split into two functions one accepting
/// owed and the other borrowed values.
///
/// # Alternatives
///
/// If you mainly have values implementing `ToOwned` like `&str`/`String`, `Path`/`PathBuf` or
/// `&[T]`/`Vec<T>` using `std::borrow::Cow` might be preferable.
///
/// If you want to be able to treat `&T`, `&mut T`, `Box<T>` and `Arc<T>` the same
/// consider using [`reffers::rbma::RBMA`](https://docs.rs/reffers)
/// (through not all types/platforms are supported because
/// as it relies on the fact that for many pointers the lowest two bits are 0, and stores
/// the discriminant in them, nevertheless this is can only be used with 32bit-aligned data,
/// e.g. using a &u8 _might_ fail). RBMA also allows you to recover a `&mut T` if it was created
/// from `Box<T>`, `&mut T` or a unique `Arc`.
///
///
/// # Examples
///
/// ```
/// # use maybe_owned::MaybeOwned;
/// struct PseudoBigData(u8);
/// fn pseudo_register_fn<'a, E>(_val: E) where E: Into<MaybeOwned<'a, PseudoBigData>> { }
///
/// let data = PseudoBigData(12);
/// let data2 = PseudoBigData(13);
///
/// pseudo_register_fn(&data);
/// pseudo_register_fn(&data);
/// pseudo_register_fn(data2);
/// pseudo_register_fn(MaybeOwned::Owned(PseudoBigData(111)));
/// ```
///
/// ```
/// # use maybe_owned::MaybeOwned;
/// #[repr(C)]
/// struct OpaqueFFI {
/// ref1: * const u8
/// //we also might want to have PhantomData etc.
/// }
///
/// // does not work as it does not implement `ToOwned`
/// // let _ = Cow::Owned(OpaqueFFI { ref1: 0 as *const u8});
///
/// // ok, MaybeOwned can do this (but can't do &str<->String as tread of)
/// let _ = MaybeOwned::Owned(OpaqueFFI { ref1: 0 as *const u8 });
/// ```
///
/// ```
/// # #[macro_use]
/// # extern crate serde_derive;
/// # extern crate serde_json;
/// # extern crate maybe_owned;
/// # #[cfg(feature = "serde")]
/// # fn main() {
/// # use maybe_owned::MaybeOwned;
/// use std::collections::HashMap;
///
/// #[derive(Serialize, Deserialize)]
/// struct SerializedData<'a> {
/// data: MaybeOwned<'a, HashMap<String, i32>>,
/// }
///
/// let mut map = HashMap::new();
/// map.insert("answer".to_owned(), 42);
///
/// // serializing can use borrowed data to avoid unnecessary copying
/// let bytes = serde_json::to_vec(&SerializedData { data: (&map).into() }).unwrap();
///
/// // deserializing creates owned data
/// let deserialized: SerializedData = serde_json::from_slice(&bytes).unwrap();
/// assert_eq!(deserialized.data["answer"], 42);
/// # }
/// # #[cfg(not(feature = "serde"))] fn main() {}
/// ```
///
/// # Transitive `std::ops` implementations
///
/// There are transitive implementations for most operator in `std::ops`.
///
/// A Op between a `MaybeOwned<L>` and `MaybeOwned<R>` is implemented if:
///
/// - L impl the Op with R
/// - L impl the Op with &R
/// - &L impl the Op with R
/// - &L impl the Op with &R
/// - the `Output` of all aboves implementations is
/// the same type
///
///
/// The `Neg` (`-` prefix) op is implemented for `V` if:
///
/// - `V` impl `Neg`
/// - `&V` impl `Neg`
/// - both have the same `Output`
///
///
/// The `Not` (`!` prefix) op is implemented for `V` if:
///
/// - `V` impl `Not`
/// - `&V` impl `Not`
/// - both have the same `Output`
///
/// Adding implementations for Ops which add a `MaybeOwned` to
/// a non `MaybeOwned` value (like `MaybeOwned<T> + T`) requires
/// far reaching specialization in rust and is therefore not done
/// for now.
#[derive(Debug)]
pub enum MaybeOwned<'a, T: 'a> {
/// owns T
Owned(T),
/// has a reference to T
Borrowed(&'a T)
}
impl<'a, T> MaybeOwned<'a, T> {
/// returns true if the data is owned else false
pub fn is_owned(&self) -> bool {
match *self {
Owned(_) => true,
Borrowed(_) => false
}
}
}
impl<'a, T> Deref for MaybeOwned<'a, T> {
type Target = T;
fn deref(&self) -> &T {
match *self {
Owned(ref v) => v,
Borrowed(v) => v
}
}
}
impl<'a, T> AsRef<T> for MaybeOwned<'a, T> {
fn as_ref(&self) -> &T {
self
}
}
impl<'a, T> Borrow<T> for MaybeOwned<'a, T> {
fn borrow(&self) -> &T {
self
}
}
impl<'a, T> From<&'a T> for MaybeOwned<'a, T> {
fn from(v: &'a T) -> MaybeOwned<'a, T> {
Borrowed(v)
}
}
impl<'a, T> From<T> for MaybeOwned<'a, T> {
fn from(v: T) -> MaybeOwned<'a, T> {
Owned(v)
}
}
impl<'a, T> From<Cow<'a, T>> for MaybeOwned<'a, T>
where T: ToOwned<Owned=T>,
{
fn from(cow: Cow<'a, T>) -> MaybeOwned<'a, T> {
match cow {
Cow::Owned(v) => MaybeOwned::Owned(v),
Cow::Borrowed(v) => MaybeOwned::Borrowed(v),
}
}
}
impl<'a, T> Into<Cow<'a, T>> for MaybeOwned<'a, T>
where T: ToOwned<Owned=T>,
{
fn into(self) -> Cow<'a, T> {
match self {
MaybeOwned::Owned(v) => Cow::Owned(v),
MaybeOwned::Borrowed(v) => Cow::Borrowed(v),
}
}
}
impl<'a, T> Default for MaybeOwned<'a, T> where T: Default {
fn default() -> Self {
Owned(T::default())
}
}
impl<'a, T> Clone for MaybeOwned<'a, T> where T: Clone {
fn clone(&self) -> MaybeOwned<'a, T> {
match *self {
Owned(ref v) => Owned(v.clone()),
Borrowed(v) => Borrowed(v)
}
}
}
impl<'a, 'b, A, B> PartialEq<MaybeOwned<'b, B>> for MaybeOwned<'a, A>
where A: PartialEq<B>
{
#[inline]
fn eq(&self, other: &MaybeOwned<'b, B>) -> bool {
PartialEq::eq(self.deref(), other.deref())
}
}
impl<'a, T> Eq for MaybeOwned<'a, T> where T: Eq {}
impl<'a, T> PartialOrd for MaybeOwned<'a, T>
where T: PartialOrd
{
#[inline]
fn partial_cmp(&self, other: &MaybeOwned<'a, T>) -> Option<Ordering> {
PartialOrd::partial_cmp(self.deref(), other.deref())
}
}
impl<'a, T> Ord for MaybeOwned<'a, T>
where T: Ord
{
#[inline]
fn cmp(&self, other: &MaybeOwned<'a, T>) -> Ordering {
Ord::cmp(self.deref(), other.deref())
}
}
impl<'a, T> Hash for MaybeOwned<'a, T>
where T: Hash
{
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
Hash::hash(self.deref(), state)
}
}
impl<'a, T> fmt::Display for MaybeOwned<'a, T>
where T: fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Owned(ref o) => fmt::Display::fmt(o, f),
Borrowed(b) => fmt::Display::fmt(b, f),
}
}
}
impl<'a, T> FromStr for MaybeOwned<'a, T>
where T: FromStr
{
type Err = T::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(MaybeOwned::Owned(T::from_str(s)?))
}
}
impl<'a, T> MaybeOwned<'a, T> where T: Clone {
/// Aquires a mutable reference to owned data.
///
/// Clones data if it is not already owned.
///
/// ## Example
///
/// ```
/// use maybe_owned::MaybeOwned;
///
/// #[derive(Clone, Debug, PartialEq, Eq)]
/// struct PseudoBigData(u8);
///
/// let data = PseudoBigData(12);
///
/// let mut maybe: MaybeOwned<PseudoBigData> = (&data).into();
/// assert_eq!(false, maybe.is_owned());
///
/// {
/// let reference = maybe.to_mut();
/// assert_eq!(&mut PseudoBigData(12), reference);
/// }
/// assert!(maybe.is_owned());
/// ```
///
pub fn to_mut(&mut self) -> &mut T {
match *self {
Owned(ref mut v) => v,
Borrowed(v) => {
*self = Owned(v.clone());
match *self {
Owned(ref mut v) => v,
Borrowed(..) => unreachable!()
}
}
}
}
/// Extracts the owned data.
///
/// If the data is borrowed it is cloned before being extracted.
pub fn into_owned(self) -> T {
match self {
Owned(v) => v,
Borrowed(v) => v.clone()
}
}
}
#[cfg(test)]
mod tests {
use super::MaybeOwned;
type TestType = Vec<()>;
fn with_into<'a, I: Into<MaybeOwned<'a, TestType>>>(v: I) -> MaybeOwned<'a, TestType> {
v.into()
}
#[test]
fn is_owned() {
let data = TestType::default();
assert!(MaybeOwned::Owned(data).is_owned());
}
#[test]
fn into_with_owned() {
//ty check if it accepts references
let data = TestType::default();
assert!(with_into(data).is_owned())
}
#[test]
fn into_with_borrow() {
//ty check if it accepts references
let data = TestType::default();
assert!(!with_into(&data).is_owned());
}
#[test]
fn clone_owned() {
let maybe = MaybeOwned::<TestType>::default();
assert!(maybe.clone().is_owned());
}
#[test]
fn clone_borrow() {
let data = TestType::default();
let maybe: MaybeOwned<TestType> = (&data).into();
assert!(!maybe.clone().is_owned());
}
#[test]
fn to_mut() {
let data = TestType::default();
let mut maybe: MaybeOwned<TestType> = (&data).into();
assert!(!maybe.is_owned());
{
let _mut_ref = maybe.to_mut();
}
assert!(maybe.is_owned());
}
#[test]
fn into_inner() {
let data = vec![1u32,2];
let maybe: MaybeOwned<Vec<u32>> = (&data).into();
assert_eq!(data, maybe.into_owned());
}
#[test]
fn has_default() {
#[derive(Default)]
struct TestType(u8);
let _x: MaybeOwned<TestType> = Default::default();
}
#[test]
fn has_clone() {
#[derive(Clone)]
struct TestType(u8);
let _x = TestType(12).clone();
}
#[test]
fn has_partial_eq() {
#[derive(PartialEq)]
struct TestType(f32);
let n = TestType(33.0);
let a = MaybeOwned::Owned(TestType(42.0));
let b = MaybeOwned::Borrowed(&n);
let c = MaybeOwned::Owned(TestType(33.0));
assert_eq!(a == b, false);
assert_eq!(b == c, true);
assert_eq!(c == a, false);
}
#[test]
fn has_eq() {
#[derive(PartialEq, Eq)]
struct TestType(i32);
let n = TestType(33);
let a = MaybeOwned::Owned(TestType(42));
let b = MaybeOwned::Borrowed(&n);
let c = MaybeOwned::Owned(TestType(33));
assert_eq!(a == b, false);
assert_eq!(b == c, true);
assert_eq!(c == a, false);
}
#[test]
fn has_partial_ord() {
#[derive(PartialEq, PartialOrd)]
struct TestType(f32);
let n = TestType(33.0);
let a = MaybeOwned::Owned(TestType(42.0));
let b = MaybeOwned::Borrowed(&n);
let c = MaybeOwned::Owned(TestType(33.0));
assert_eq!(a > b, true);
assert_eq!(b > c, false);
assert_eq!(a < c, false);
}
#[test]
fn has_ord() {
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct TestType(i32);
let n = TestType(33);
let a = MaybeOwned::Owned(TestType(42));
let b = MaybeOwned::Borrowed(&n);
let c = MaybeOwned::Owned(TestType(33));
assert_eq!(a > b, true);
assert_eq!(b > c, false);
assert_eq!(a < c, false);
}
#[test]
fn has_hash() {
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert(MaybeOwned::Owned(42), 33);
assert_eq!(map.get(&MaybeOwned::Borrowed(&42)), Some(&33));
}
#[test]
fn has_display() {
let n = 33;
let a = MaybeOwned::Owned(42);
let b = MaybeOwned::Borrowed(&n);
let s = format!("{} {}", a, b);
assert_eq!(s, "42 33");
}
#[test]
fn from_cow() {
use std::borrow::Cow;
fn test<'a, V: Into<MaybeOwned<'a, i32>>>(v: V, n: i32) { assert_eq!(*v.into(), n) }
let n = 33;
test(Cow::Owned(42), 42);
test(Cow::Borrowed(&n), n);
}
#[test]
fn into_cow() {
use std::borrow::Cow;
fn test<'a, V: Into<Cow<'a, i32>>>(v: V, n: i32) { assert_eq!(*v.into(), n) }
let n = 33;
test(MaybeOwned::Owned(42), 42);
test(MaybeOwned::Borrowed(&n), n);
}
#[test]
fn from_str() {
let as_string = "12";
//assumption as_string is convertable to u32
assert_eq!(12u32, as_string.parse().unwrap());
assert_eq!(MaybeOwned::Owned(12u32), as_string.parse().unwrap());
}
#[test]
fn as_ref() {
let data = TestType::default();
let maybe_owned = MaybeOwned::Borrowed(&data);
let _ref: &TestType = maybe_owned.as_ref();
assert_eq!(
&data as *const _ as usize,
_ref as *const _ as usize
);
}
#[test]
fn borrow() {
use std::borrow::Borrow;
let data = TestType::default();
let maybe_owned = MaybeOwned::Borrowed(&data);
let _ref: &TestType = maybe_owned.borrow();
assert_eq!(
&data as *const _ as usize,
_ref as *const _ as usize
);
}
}