trait_union/lib.rs
1#![cfg_attr(not(test), no_std)]
2#![cfg_attr(test, feature(untagged_unions))]
3
4//! This crate provides a macro that generates a trait-union type. That is, a trait
5//! object type which can contain any one of a pre-determined set of implementors.
6//!
7//! The generated type does not allocate. The size of the type is the size of the largest
8//! variant plus some constant overhead.
9//!
10//! **NOTE**: As of rustc 1.47, you must enable the `untagged_unions` feature to store
11//! non-[Copy] types in a trait-union. This will change
12//! [soon](https://github.com/rust-lang/rust/pull/77547).
13//!
14//! # Example
15//!
16//! ```rust
17//! # use trait_union::trait_union;
18//! # use std::fmt::Display;
19//! #
20//! trait_union! {
21//! /// Container can contain either an i32, a &'static str, or a bool.
22//! union Container: Display = i32 | &'static str | bool;
23//! }
24//!
25//! let mut container = Container::new(32);
26//! assert_eq!(container.to_string(), "32");
27//!
28//! container = Container::new("Hello World");
29//! assert_eq!(container.to_string(), "Hello World");
30//!
31//! container = Container::new(true);
32//! assert_eq!(container.to_string(), "true");
33//! ```
34//!
35//! The generated type has the following interface:
36//!
37//! ```rust,ignore
38//! struct Container {
39//! /* ... */
40//! }
41//!
42//! impl Container {
43//! fn new(value: impl ContainerVariant) -> Self { /* ... */ }
44//! }
45//!
46//! impl Deref for Container {
47//! type Target = dyn Display + 'static;
48//! /* ... */
49//! }
50//!
51//! impl DerefMut for Container {
52//! /* ... */
53//! }
54//!
55//! unsafe trait ContainerVariant: Display + 'static { }
56//!
57//! unsafe impl ContainerVariant for i32 { }
58//! unsafe impl ContainerVariant for &'static str { }
59//! unsafe impl ContainerVariant for bool { }
60//! ```
61
62/// Macro that generates a trait-union type
63///
64/// # Syntax
65///
66/// Each invocation of the macro can generate an arbitrary number of trait-union types.
67///
68/// The syntax of each declaration is as follows:
69///
70/// ```txt
71/// ATTRIBUTE* VISIBILITY? 'union' NAME GENERICS? ':' TRAIT_BOUNDS ('where' WHERE_CLAUSE)? '=' TYPE ('|' TYPE)* '|'? ';'
72/// ```
73///
74/// `?` denotes an optional segment. `*` denotes 0 or more repetitions.
75///
76/// For example:
77///
78/// ```rust,ignore
79/// /// MyUnion trait-union
80/// pub(crate) union MyUnion<'a, T: 'a>: Debug+'a where T: Debug+Copy = &'a str | Option<T>;
81/// ```
82///
83/// # Trait bounds
84///
85/// The `TRAIT_BOUNDS` segment denotes the trait that the trait-union will deref to. As
86/// such, it must contain at least one trait, at most one non-auto trait, and 0 or more
87/// lifetimes.
88///
89/// For example:
90///
91/// ```rust,ignore
92/// Debug+Copy+'a // OK
93/// 'a // Error: No trait
94/// Debug+Display // Error: More than one non-auto trait
95/// ```
96///
97/// If you do not provide a lifetime, the `'static` lifetime will be added automatically.
98/// That is, `Debug` is the same as `Debug+'static`. For example
99///
100/// ```rust,ignore
101/// union MyUnion<'a>: Debug = &'a str;
102/// ```
103///
104/// will not compile because `&'a str` is not `'static`. Write
105///
106/// ```rust,ignore
107/// union MyUnion<'a>: Debug+'a = &'a str;
108/// ```
109///
110/// instead.
111///
112/// # Output
113///
114/// The macro generates a struct with the specified name and an unsafe trait of the same
115/// name plus the suffix `Variant`. For example
116///
117/// ```rust,ignore
118/// pub(crate) union MyUnion<'a, T: 'a>: Debug+'a where T: Debug+Copy = &'a str | Option<T>
119/// ```
120///
121/// generates
122///
123/// ```rust,ignore
124/// pub(crate) struct MyUnion<'a, T: 'a> where T: Debug+Copy {
125/// /* ... */
126/// }
127///
128/// pub(crate) unsafe trait MyUnionVariant<'a, T: 'a>: Debug+'a where T: Debug+Copy { }
129/// ```
130///
131/// The trait will automatically be implemented for all specified variants. The struct has
132/// a single associated method:
133///
134/// ```rust,ignore
135/// pub(crate) fn new(value: impl MyUnionVariant<'a, T>) -> Self { /* ... */ }
136/// ```
137///
138/// The struct implements `Deref` and `DerefMut` with `Target = Debug+'a`.
139pub use trait_union_proc::trait_union;
140
141/// Macro that generates a trait-union type for [Copy] implementors
142///
143/// This macro is identical to [trait_union] except that
144///
145/// - all implementors must be [Copy]
146/// - the generated type is not [Drop]
147/// - `#[derive(Copy, Clone)]` can be used as an attribute
148pub use trait_union_proc::trait_union_copy;
149
150#[cfg(test)]
151mod test {
152 use super::{trait_union, trait_union_copy};
153 use std::{
154 fmt,
155 fmt::{Display, Formatter},
156 mem,
157 sync::atomic::{AtomicUsize, Ordering::Relaxed},
158 };
159
160 trait F: Display {
161 fn len(&self) -> usize;
162
163 fn set_len(&mut self, len: usize);
164 }
165
166 impl F for u8 {
167 fn len(&self) -> usize {
168 *self as usize
169 }
170
171 fn set_len(&mut self, len: usize) {
172 *self = len as u8;
173 }
174 }
175
176 impl F for String {
177 fn len(&self) -> usize {
178 self.len()
179 }
180
181 fn set_len(&mut self, len: usize) {
182 self.truncate(len);
183 }
184 }
185
186 #[repr(align(4))]
187 struct X;
188 impl F for X {
189 fn len(&self) -> usize {
190 !0
191 }
192
193 fn set_len(&mut self, len: usize) {
194 X_DROP_COUNT.store(len, Relaxed);
195 }
196 }
197 static X_DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
198 impl Drop for X {
199 fn drop(&mut self) {
200 X_DROP_COUNT.fetch_add(1, Relaxed);
201 }
202 }
203 impl Display for X {
204 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
205 write!(f, "X")
206 }
207 }
208
209 trait_union! {
210 union U: F = u8 | String | X;
211 }
212
213 #[test]
214 fn test1() {
215 let mut c = U::new(33);
216 assert_eq!(mem::align_of_val(&*c), 1);
217 assert_eq!(mem::size_of_val(&*c), 1);
218 assert!(mem::align_of_val(&c) >= 4);
219 assert!(mem::size_of_val(&c) >= 4);
220 assert_eq!(c.len(), 33);
221 c.set_len(22);
222 assert_eq!(c.len(), 22);
223 c = U::new("Hello World".to_string());
224 assert_eq!(c.len(), 11);
225 c.set_len(5);
226 assert_eq!(c.len(), 5);
227 assert_eq!(c.to_string(), "Hello");
228 c = U::new(X);
229 assert_eq!(mem::align_of_val(&*c), 4);
230 assert_eq!(mem::size_of_val(&*c), 0);
231 assert_eq!(c.len(), !0);
232 assert_eq!(X_DROP_COUNT.load(Relaxed), 0);
233 c.set_len(2);
234 assert_eq!(X_DROP_COUNT.load(Relaxed), 2);
235 drop(c);
236 assert_eq!(X_DROP_COUNT.load(Relaxed), 3);
237 }
238
239 #[test]
240 fn size() {
241 assert_eq!(mem::size_of::<U>(), mem::size_of::<Option<U>>());
242 }
243
244 #[test]
245 fn compile() {
246 let t = trybuild::TestCases::new();
247 t.compile_fail("tests/compile-fail/*.rs");
248 t.pass("tests/pass/*.rs");
249 }
250
251 #[test]
252 fn copy() {
253 trait_union_copy! {
254 #[derive(Copy, Clone)]
255 union U: Display = u8 | &'static str;
256 }
257
258 let u = U::new("test");
259 let v = u;
260 assert_eq!(u.to_string(), v.to_string());
261 }
262
263 #[test]
264 fn assert_sync() {
265 let _: &dyn Sync = &U::new(1);
266 }
267}