pointer_value_pair/
cow.rs

1use std::marker::PhantomData;
2use std::mem;
3use std::ops::Deref;
4use crate::PointerValuePair;
5
6/// A pointer-sized object that holds either a borrow (`&'a T`) or a boxed value (`Box<T>`).
7///
8/// TODO doc: implements deref, construction, ToOwned, etc.
9///
10/// # Notes
11///
12/// Because it uses `PointerValuePair` internally, `T` cannot not be a zero-sized type.
13#[repr(transparent)]
14pub struct Cow<'a, T> {
15    inner: PointerValuePair<T>,
16    _phantom: PhantomData<&'a T>,
17}
18
19const BORROWED: usize = 0usize;
20const OWNED: usize = 1usize;
21
22impl<'a, T> Cow<'a, T> {
23    /// Creates a new `Cow` representing a borrowed value.
24    pub fn borrowed(v: &'a T) -> Cow<'a, T> {
25        Cow {
26            inner: PointerValuePair::new(v, BORROWED),
27            _phantom: PhantomData,
28        }
29    }
30
31    /// Creates a new `Cow` holding a boxed value.
32    pub fn owned(v: Box<T>) -> Cow<'a, T> {
33        Cow {
34            inner: PointerValuePair::new(Box::into_raw(v), OWNED),
35            _phantom: PhantomData,
36        }
37    }
38}
39
40impl<'a, T> Cow<'a, T> where T: Clone {
41    /// Converts this `Cow` into a `Box<T>`. If this `Cow` is a borrow, clones the value and boxes it.
42    pub fn into_owned(self) -> Box<T> {
43        if self.inner.value() == OWNED {
44            let boxed = unsafe {
45                // SAFETY: the pointer has been created with `Box::into_raw` by `Cow::owned`.
46                // We inhibit drop by calling mem::forget below.
47                Box::from_raw(self.inner.ptr() as *mut T)
48            };
49            // we extracted the boxed value already, don't double-drop
50            mem::forget(self);
51            boxed
52        } else {
53            Box::new(self.deref().clone())
54        }
55    }
56
57    /// Converts this `Cow` into an owned `Cow` by cloning the value and boxing it, if it is borrowed.
58    pub fn into_owned_cow<'b>(self) -> Cow<'b, T> {
59        if self.inner.value() == OWNED {
60            // We own the value, so it's OK to just transfer it
61            let result = Cow {
62                inner: self.inner,
63                _phantom: Default::default()
64            };
65            // we transferred ownership of the box, don't double-drop
66            mem::forget(self);
67            result
68        } else {
69            Cow::owned(Box::new(self.deref().clone()))
70        }
71    }
72}
73
74
75impl<'a, T> Drop for Cow<'a, T> {
76    fn drop(&mut self) {
77        unsafe {
78            if self.inner.value() == OWNED {
79                drop(Box::from_raw(self.inner.ptr() as *mut T))
80            }
81        }
82    }
83}
84
85impl<'a, T> Deref for Cow<'a, T> {
86    type Target = T;
87
88    fn deref(&self) -> &Self::Target {
89        // SAFETY: ptr is either a pointer to a boxed value for which we are the owner (and are responsible for the deletion),
90        // or a pointer to a borrowed value, whose validity is ensured by the lifetime bound.
91        unsafe { &*self.inner.ptr() }
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use std::cell::Cell;
98    use std::mem;
99    use crate::Cow;
100
101    #[test]
102    fn pointer_sized() {
103        assert_eq!(mem::size_of::<*const i32>(), mem::size_of::<Cow<'static,i32>>());
104    }
105
106    #[test]
107    fn owned_cow_drop() {
108        let drop_flag = Cell::new(false);
109
110        #[derive(Clone)]
111        struct DropTest<'a> {
112            flag: &'a Cell<bool>
113        }
114
115        impl<'a> Drop for DropTest<'a> {
116            fn drop(&mut self) {
117                self.flag.set(true)
118            }
119        }
120
121        {
122            let drop_test = DropTest { flag: &drop_flag };
123            let cow = Cow::owned(Box::new(drop_test));
124            let cow = cow.into_owned_cow();
125            assert!(!drop_flag.get());
126            let boxed  = cow.into_owned();
127            assert!(!drop_flag.get());
128            let _cow = Cow::owned(boxed);
129            assert!(!drop_flag.get());
130        }
131
132        assert!(drop_flag.get());
133    }
134}