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}