structz/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![deny(missing_docs)]
3#![no_std]
4
5//! Anonymous struct implementation in rust.
6//!
7//! # Overview
8//!
9//! ## Install
10//!
11//! ```bash
12//! cargo add structz
13//! ```
14//!
15//! ## Create & access
16//!
17//! ```
18//! use structz::*;
19//!
20//! let age = 26;
21//! let mut person = stru! {
22//!     name: "John Doe",
23//!     age,    // captures the value of the variable with the same name
24//!     tags: vec!["developer", "rustacean"],
25//! };
26//!
27//! // immutable borrow
28//! assert_eq!(field!(&person.name), &"John Doe");
29//!
30//! // mutable borrow
31//! *field!(&mut person.age) += 1;
32//! assert_eq!(field!(&person.age), &27);
33//!
34//! // consume the struct and get the field value
35//! let tags = field!(person.tags);
36//! assert_eq!(tags, vec!["developer", "rustacean"]);
37//!
38//! // `person` cannot be used anymore.
39//! ```
40//!
41//! **NOTE**: anonymous structs with the same fields but different field orders
42//! are considered structs of the same type.
43//!
44//! ```
45//! use structz::*;
46//!
47//! let rect1 = stru! { width: 1920, height: 1080 };
48//! let rect2 = stru! { height: 1080, width: 1920 };
49//! assert_eq!(rect1, rect2);
50//! ```
51//!
52//! ## As argument
53//!
54//! ```
55//! use structz::*;
56//!
57//! fn print_person(person: stru_t! {name: &str, age: u8, tags: Vec<&str> }) {
58//!     println!(
59//!         "{} is {} years old and has tags {:?}",
60//!         field!(&person.name),
61//!         field!(&person.age),
62//!         field!(&person.tags)
63//!     );
64//! }
65//!
66//! let person = stru! {
67//!     tags: vec!["programmer", "artist"],
68//!     name: "Alice",
69//!     age: 30,
70//! };
71//! print_person(person);
72//! ```
73//!
74//! A better way is to use the [`macro@named_args`] macro which helps you unpack the struct:
75//!
76//! ```
77//! use structz::*;
78//!
79//! #[named_args]
80//! fn print_person(name: &str, age: u8, tags: Vec<&str>) {
81//!     println!("{} is {} years old and has tags {:?}", name, age, tags);
82//! }
83//!
84//! let person = stru! {
85//!     tags: vec!["programmer", "artist"],
86//!     name: "Alice",
87//!     age: 30,
88//! };
89//! print_person(person);
90//! ```
91//!
92//! With the [`subseq()`](https://docs.rs/tuplez/latest/tuplez/trait.TupleLike.html#method.subseq)
93//! method by [tuplez](https://docs.rs/tuplez), you can get a sub-struct of the anonymous struct:
94//!
95//! ```
96//! use structz::*;
97//! use tuplez::TupleLike;
98//!
99//! #[named_args]
100//! fn print_person(name: &str, age: u8) {
101//!     println!("{} is {} years old", name, age);
102//! }
103//!
104//! let alice = stru! {
105//!     jobs: "programmer",
106//!     name: "Alice",
107//!     age: 30,
108//!     children: vec!["Bob"],
109//! };
110//! print_person(alice.subseq());
111//!
112//! let bob = stru! {
113//!     name: "Bob",
114//!     parent: vec!["Alice", "John"],
115//!     age: 7,
116//!     grade: 1,
117//! };
118//! print_person(bob.subseq());
119//!
120//! let empty = stru! {
121//!     name: "**Empty**",
122//!     age: 0,
123//! };
124//! print_person(empty.subseq());   // Of course it is a sub-struct of itself
125//! ```
126//!
127//! ## As generic type
128//!
129//! ```
130//! use structz::*;
131//!
132//! // `R1` and `R2` are "magic", used to indicate the position of the field in the structs,
133//! // and these magic generic types will be automatically inferred by Rust.
134//! // You should introduce a magic generic type for each field.
135//! fn print_name_id<T, R1, R2>(any: &T)
136//! where
137//!     T: HasField<ident!(name), &'static str, R1>,
138//!     T: HasField<ident!(id), usize, R2>,
139//! {
140//!     println!("{}", field!(&any.name));
141//!     println!("{}", field!(&any.id));
142//! }
143//!
144//! let person = stru! {
145//!     name: "John",
146//!     age: 15,
147//!     id: 1006,
148//!     jobs: "Programmer",
149//! };
150//! let earth = stru! {
151//!     name: "Earth",
152//!     id: 3,
153//!     galaxy: "Sol",
154//!     satellites: vec!["Moon"],
155//! };
156//! print_name_id(&person);
157//! print_name_id(&earth);
158//! ```
159//!
160//! # Details
161//!
162//! The implementation of structz is based on [stringz](https://docs.rs/stringz) and [tuplez](https://docs.rs/tuplez).
163//!
164//! First, macros sort the input fields in lexicographic order, which ensures that anonymous structs
165//! with the same fields but different field orders are of the same type.
166//!
167//! Second, convert the field names into a specialized type consisting of a sequence of zero-sized types via
168//! [stringz](https://docs.rs/stringz). Let's call them "field name types".
169//!
170//! Finally, pack the field name type and the data type of each field, combine them into
171//! [tuplez](https://docs.rs/tuplez)'s [`Tuple`](https://docs.rs/tuplez/latest/tuplez/struct.Tuple.html).
172//!
173//! Since the field names is effectively replaced with a zero-sized type, it cost you nothing:
174//!
175//! ```
176//! use structz::*;
177//!
178//! assert_eq!(
179//!     std::mem::size_of::<
180//!         stru_t! {
181//!             age: u8,
182//!             name: &'static str,
183//!             tags: Vec<&'static str>,
184//!         },
185//!     >(),
186//!     std::mem::size_of::<(u8, &'static str, Vec<&'static str>)>()
187//! );
188//! ```
189
190#[macro_use]
191mod macros;
192mod has_field;
193
194pub use has_field::*;
195
196extern crate self as structz;
197
198#[doc(hidden)]
199pub use stringz as __stringz;
200#[doc(hidden)]
201pub use stringz::__tuplez;
202
203#[doc(no_inline)]
204pub use stringz::{ident, TypedString};
205
206#[doc(hidden)]
207pub use structz_macros::{stru as stru_inner, stru_t as stru_t_inner};
208
209/// Change the function's arguments to an anonymous struct object and unpack it.
210///
211/// ```
212/// use structz::*;
213///
214/// #[named_args]
215/// fn print_person(name: &str, age: u8, tags: Vec<&str>) {
216///     println!("{} is {} years old and has tags {:?}", name, age, tags);
217/// }
218///
219/// print_person(stru! {
220///     tags: vec!["programmer", "artist"],
221///     name: "Alice",
222///     age: 30,
223/// });
224/// ```
225///
226/// The method receivers are not considered part of the anonymous struct object:
227///
228/// ```
229/// use structz::*;
230///
231/// struct Num(i32);
232/// impl Num {
233///     #[named_args]
234///     fn add(&mut self, x: i32, y: i32) {
235///         self.0 += x;
236///         self.0 += y;
237///     }
238/// }
239///
240/// let mut num = Num(1);
241/// num.add(stru! { x: 2, y: 3 });
242/// assert_eq!(num.0, 6);
243/// ```
244pub use structz_macros::named_args;