bevy_utils/
cow_arc.rs

1use std::{
2    borrow::Borrow,
3    fmt::{Debug, Display},
4    hash::Hash,
5    ops::Deref,
6    path::{Path, PathBuf},
7    sync::Arc,
8};
9
10/// Much like a [`Cow`](std::borrow::Cow), but owned values are Arc-ed to make clones cheap. This should be used for values that
11/// are cloned for use across threads and change rarely (if ever).
12///
13/// This also makes an opinionated tradeoff by adding a [`CowArc::Static`] and implementing [`From<&'static T>`] instead of
14/// [`From<'a T>`]. This preserves the static context and prevents conversion to [`CowArc::Owned`] in cases where a reference
15/// is known to be static. This is an optimization that prevents allocations and atomic ref-counting.
16///
17/// This means that static references should prefer [`From::from`] or [`CowArc::Static`] and non-static references must
18/// use [`CowArc::Borrowed`].
19pub enum CowArc<'a, T: ?Sized + 'static> {
20    /// A borrowed value
21    Borrowed(&'a T),
22    /// A static value reference. This exists to avoid conversion to [`CowArc::Owned`] in cases where a reference is
23    /// known to be static. This is an optimization that prevents allocations and atomic ref-counting.
24    Static(&'static T),
25    /// An owned [`Arc`]-ed value
26    Owned(Arc<T>),
27}
28
29impl<'a, T: ?Sized> Deref for CowArc<'a, T> {
30    type Target = T;
31
32    #[inline]
33    fn deref(&self) -> &Self::Target {
34        match self {
35            CowArc::Borrowed(v) | CowArc::Static(v) => v,
36            CowArc::Owned(v) => v,
37        }
38    }
39}
40
41impl<'a, T: ?Sized> Borrow<T> for CowArc<'a, T> {
42    #[inline]
43    fn borrow(&self) -> &T {
44        self
45    }
46}
47
48impl<'a, T: ?Sized> AsRef<T> for CowArc<'a, T> {
49    #[inline]
50    fn as_ref(&self) -> &T {
51        self
52    }
53}
54
55impl<'a, T: ?Sized> CowArc<'a, T>
56where
57    &'a T: Into<Arc<T>>,
58{
59    /// Converts this into an "owned" value. If internally a value is borrowed, it will be cloned into an "owned [`Arc`]".
60    /// If it is already a [`CowArc::Owned`] or a [`CowArc::Static`], it will remain unchanged.
61    #[inline]
62    pub fn into_owned(self) -> CowArc<'static, T> {
63        match self {
64            CowArc::Borrowed(value) => CowArc::Owned(value.into()),
65            CowArc::Static(value) => CowArc::Static(value),
66            CowArc::Owned(value) => CowArc::Owned(value),
67        }
68    }
69
70    /// Clones into an owned [`CowArc<'static>`]. If internally a value is borrowed, it will be cloned into an "owned [`Arc`]".
71    /// If it is already a [`CowArc::Owned`] or [`CowArc::Static`], the value will be cloned.
72    /// This is equivalent to `.clone().into_owned()`.
73    #[inline]
74    pub fn clone_owned(&self) -> CowArc<'static, T> {
75        self.clone().into_owned()
76    }
77}
78
79impl<'a, T: ?Sized> Clone for CowArc<'a, T> {
80    #[inline]
81    fn clone(&self) -> Self {
82        match self {
83            Self::Borrowed(value) => Self::Borrowed(value),
84            Self::Static(value) => Self::Static(value),
85            Self::Owned(value) => Self::Owned(value.clone()),
86        }
87    }
88}
89
90impl<'a, T: PartialEq + ?Sized> PartialEq for CowArc<'a, T> {
91    #[inline]
92    fn eq(&self, other: &Self) -> bool {
93        self.deref().eq(other.deref())
94    }
95}
96
97impl<'a, T: PartialEq + ?Sized> Eq for CowArc<'a, T> {}
98
99impl<'a, T: Hash + ?Sized> Hash for CowArc<'a, T> {
100    #[inline]
101    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
102        self.deref().hash(state);
103    }
104}
105
106impl<'a, T: Debug + ?Sized> Debug for CowArc<'a, T> {
107    #[inline]
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        Debug::fmt(self.deref(), f)
110    }
111}
112
113impl<'a, T: Display + ?Sized> Display for CowArc<'a, T> {
114    #[inline]
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        Display::fmt(self.deref(), f)
117    }
118}
119
120impl<'a, T: PartialOrd + ?Sized> PartialOrd for CowArc<'a, T> {
121    #[inline]
122    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
123        self.deref().partial_cmp(other.deref())
124    }
125}
126
127impl Default for CowArc<'static, str> {
128    fn default() -> Self {
129        CowArc::Static(Default::default())
130    }
131}
132
133impl Default for CowArc<'static, Path> {
134    fn default() -> Self {
135        CowArc::Static(Path::new(""))
136    }
137}
138
139impl<'a, T: Ord + ?Sized> Ord for CowArc<'a, T> {
140    #[inline]
141    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
142        self.deref().cmp(other.deref())
143    }
144}
145
146impl From<PathBuf> for CowArc<'static, Path> {
147    #[inline]
148    fn from(value: PathBuf) -> Self {
149        CowArc::Owned(value.into())
150    }
151}
152
153impl From<&'static str> for CowArc<'static, Path> {
154    #[inline]
155    fn from(value: &'static str) -> Self {
156        CowArc::Static(Path::new(value))
157    }
158}
159
160impl From<String> for CowArc<'static, str> {
161    #[inline]
162    fn from(value: String) -> Self {
163        CowArc::Owned(value.into())
164    }
165}
166
167impl<'a> From<&'a String> for CowArc<'a, str> {
168    #[inline]
169    fn from(value: &'a String) -> Self {
170        CowArc::Borrowed(value)
171    }
172}
173
174impl<T: ?Sized> From<&'static T> for CowArc<'static, T> {
175    #[inline]
176    fn from(value: &'static T) -> Self {
177        CowArc::Static(value)
178    }
179}