├── .gitignore ├── README.md ├── Cargo.toml └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # new_type 2 | 3 | Easily construct newtypes in Rust. :) 4 | 5 | Usage might be dangerous. :( 6 | 7 | Head over to the docs at: https://docs.rs/new_type -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "new_type" 3 | version = "0.4.1" 4 | authors = ["Silvan Buedenbender "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "Experimental implementation of newtypes by type level logic." 8 | homepage = "https://github.com/SilvanCodes/new_type" 9 | repository = "https://github.com/SilvanCodes/new_type" 10 | readme = "README.md" 11 | keywords = ["macro", "newtype"] 12 | categories = ["rust-patterns"] 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Welcome, fellow programmer! 👋🏼 2 | //! 3 | //! The newtype macro is very simple: 4 | //! 5 | //! ```rust 6 | //! # #[macro_use] extern crate new_type; 7 | //! # fn main() { 8 | //! newtype!(Meters, Centimeters); 9 | //! 10 | //! let distance_one = Meters(10); 11 | //! let distance_two = Meters(5); 12 | //! let distance_three = Centimeters(5); 13 | //! 14 | //! // Successfully add the same type. 15 | //! assert_eq!(distance_one + distance_two, Meters(15)) 16 | //! 17 | //! // Compilation error: expected struct `Meters`, found struct `Centimeters` 18 | //! // assert_eq!(distance_one + distance_three, Meters(15)) 19 | //! # } 20 | //! ``` 21 | //! 22 | //! # Under the hood ⚙️ 23 | //! 24 | //! The newtype is implemented with a generic type parameter. 25 | //! Via this type parameter it does kind of a "type level derive" i.e. it implements operations by deferring to the implementation of the underlying type. 26 | //! That works across any level of nested newtypes, if that should be of any use. 27 | //! For details read on in [newtype]. 28 | //! 29 | //! # Footgun Warning ⚠️ 30 | //! 31 | //! It is highly recommended to explicitly pass the expexted types into the tuple constructor as by default any type goes due to the generic type parameter. 32 | //! This is definetly a footgun but as the generic type parameter is core to how this crate operates not obviously avoidable. Open a PR is you have ideas. 33 | //! 34 | //! ```rust 35 | //! # #[macro_use] extern crate new_type; 36 | //! # fn main() { 37 | //! // We specify a default type. 38 | //! // The default type is only relevant for calls to T::default() and only if there is no reason for the compiler to infer another type. 39 | //! newtype!(Meters: f32); 40 | //! 41 | //! // Footgun right here, we pass an integer, so it will be an i32. 42 | //! let distance_one = Meters(10); 43 | //! 44 | //! // Footgun possible here, we pass a float, so it would be a f64 by default. 45 | //! // Because we add it with an explicit f32 type the compiler can infer it needs to be an f32 as well. 46 | //! let distance_two = Meters(5.0); 47 | //! 48 | //! // The recommended way to construct values of the newtype. 49 | //! let distance_three = Meters(5.0_f32); 50 | //! 51 | //! assert_eq!(distance_two + distance_three, Meters(10.0)); 52 | //! 53 | //! // Compilation error: expected integer, found floating-point number 54 | //! // assert_eq!(distance_one + distance_two, Meters(15)) 55 | //! 56 | //! // Footgun here, f64 is infered for the call to T::default() despite f32 being the default type parameter. 57 | //! assert_eq!(Meters::default(), Meters(0.0_f64)); 58 | //! # } 59 | //! ``` 60 | 61 | /// Implements its arguments as newtypes. 62 | /// 63 | /// The macro is meant to provide easy means to enhance the semantics of language built-ins. 64 | /// 65 | /// Newtypes come with `Deref`, `DerefMut`, `AsRef`, `AsMut`, and `From` traits. 66 | /// Further they implement almost all std::ops and std::cmp of the type they wrap if the operants have value semantics and return `Self`. 67 | /// Exceptions are std::ops::{`Drop`, `Fn`, `FnMut`, `FnOnce`, `Index`, `IndexMut`, `RangeBounds`}. 68 | /// 69 | /// Usually one obtains instances of the newtype by the public constructor but `Default` is available if the wrapped type implements it. 70 | /// It is not as ergonomic as it should be though, see examples below. 71 | /// 72 | /// # Examples 73 | /// 74 | /// Operations are available on newtypes: 75 | /// ```rust 76 | /// # #[macro_use] extern crate new_type; 77 | /// # fn main() { 78 | /// newtype!(Count); 79 | /// let count_one = Count(100); 80 | /// let count_two = Count(50); 81 | /// // We can add 'Count' because we can add i32! 82 | /// assert_eq!(count_one + count_two, Count(150)) 83 | /// # } 84 | /// ``` 85 | /// Newtypes can be simple function arguments with default types: 86 | /// ```rust 87 | /// # #[macro_use] extern crate new_type; 88 | /// # fn main() { 89 | /// // We specify default type here! 90 | /// newtype!(Count: usize); 91 | /// 92 | /// fn add_count(a: Count, b: Count) -> Count { 93 | /// a + b 94 | /// } 95 | /// 96 | /// // We can add 'Count' because we can add usize! 97 | /// assert_eq!(add_count(Count(100), Count(50)), Count(150)) 98 | /// # } 99 | /// ``` 100 | /// Functions and defaults are available on newtypes: 101 | /// ```rust 102 | /// # #[macro_use] extern crate new_type; 103 | /// # use std::collections::HashSet; 104 | /// # fn main() { 105 | /// newtype!(Humans: HashSet<&'static str>); 106 | /// // Sadly we need to specify `Humans` as type in order to get access to `Default`. 107 | /// let mut some_humans: Humans = Humans::default(); 108 | /// some_humans.insert("Maria"); 109 | /// some_humans.insert("Peter"); 110 | /// let mut other_humans: Humans = Humans::default(); 111 | /// other_humans.insert("Kim"); 112 | /// other_humans.insert("Mia"); 113 | /// // We can extend Humans with Humans! 114 | /// some_humans.extend(other_humans.iter()); 115 | /// // We can ask for '.len()' on Humans because we can ask for '.len()' on HashSet! 116 | /// assert_eq!(some_humans.len(), 4) 117 | /// # } 118 | /// ``` 119 | /// Newtypes can be nested: 120 | /// ```rust 121 | /// # #[macro_use] extern crate new_type; 122 | /// # fn main() { 123 | /// newtype!(A, B, C); 124 | /// let abc_one = A(B(C(5))); 125 | /// let abc_two = A(B(C(5))); 126 | /// // We can add nested newtypes because we can add the wrapped type! 127 | /// assert_eq!(abc_one + abc_two, A(B(C(10)))) 128 | /// # } 129 | /// ``` 130 | #[macro_export] 131 | macro_rules! newtype { 132 | ( $( $newtype:ident $( : $default:ty )? ),* ) => { 133 | $( 134 | #[derive(Debug)] 135 | pub struct $newtype(pub T); 136 | 137 | impl> std::iter::FromIterator for $newtype { 138 | fn from_iter>(iter: I) -> Self { 139 | Self(T::from_iter(iter)) 140 | } 141 | } 142 | 143 | impl std::default::Default for $newtype { 144 | fn default() -> Self { 145 | Self(T::default()) 146 | } 147 | } 148 | 149 | impl std::convert::From for $newtype { 150 | fn from(other: T) -> Self { 151 | Self(other) 152 | } 153 | } 154 | 155 | impl std::ops::Deref for $newtype { 156 | type Target = T; 157 | 158 | fn deref(&self) -> &Self::Target { 159 | &self.0 160 | } 161 | } 162 | 163 | impl std::ops::DerefMut for $newtype { 164 | fn deref_mut(&mut self) -> &mut Self::Target { 165 | &mut self.0 166 | } 167 | } 168 | 169 | impl std::convert::AsRef for $newtype { 170 | fn as_ref(&self) -> &T { 171 | &self.0 172 | } 173 | } 174 | 175 | impl std::convert::AsMut for $newtype { 176 | fn as_mut(&mut self) -> &mut T { 177 | &mut self.0 178 | } 179 | } 180 | 181 | // std::clone and std::marker::Copy implementations 182 | 183 | impl std::clone::Clone for $newtype { 184 | fn clone(&self) -> Self { 185 | Self(self.0.clone()) 186 | } 187 | } 188 | 189 | impl std::marker::Copy for $newtype {} 190 | 191 | // std::cmp implementations 192 | 193 | impl std::cmp::PartialEq for $newtype { 194 | fn eq(&self, other: &Self) -> bool { 195 | self.0 == other.0 196 | } 197 | } 198 | 199 | impl std::cmp::Eq for $newtype {} 200 | 201 | impl std::cmp::PartialOrd for $newtype { 202 | fn partial_cmp(&self, other: &Self) -> Option { 203 | self.0.partial_cmp(&other.0) 204 | } 205 | } 206 | 207 | impl std::cmp::Ord for $newtype { 208 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 209 | self.0.cmp(&other.0) 210 | } 211 | } 212 | 213 | // std::hash::Hash implementation 214 | 215 | impl std::hash::Hash for $newtype { 216 | fn hash(&self, state: &mut H) { 217 | self.0.hash(state); 218 | } 219 | } 220 | 221 | // std::ops implementations 222 | 223 | impl> std::ops::Add for $newtype { 224 | type Output = Self; 225 | 226 | fn add(self, other: Self) -> Self { 227 | Self(self.0 + other.0) 228 | } 229 | } 230 | 231 | impl std::ops::AddAssign for $newtype { 232 | fn add_assign(&mut self, other: Self) { 233 | self.0 += other.0; 234 | } 235 | } 236 | 237 | impl> std::ops::BitAnd for $newtype { 238 | type Output = Self; 239 | fn bitand(self, rhs: Self) -> Self::Output { 240 | Self(self.0 & rhs.0) 241 | } 242 | } 243 | 244 | impl > std::ops::BitAndAssign for $newtype { 245 | fn bitand_assign(&mut self, rhs: Self) { 246 | self.0 &= rhs.0 247 | } 248 | } 249 | 250 | impl> std::ops::BitOr for $newtype { 251 | type Output = Self; 252 | 253 | fn bitor(self, rhs: Self) -> Self { 254 | Self(self.0 | rhs.0) 255 | } 256 | } 257 | 258 | impl std::ops::BitOrAssign for $newtype { 259 | fn bitor_assign(&mut self, rhs: Self) { 260 | self.0 |= rhs.0 261 | } 262 | } 263 | 264 | impl> std::ops::BitXor for $newtype { 265 | type Output = Self; 266 | 267 | fn bitxor(self, rhs: Self) -> Self::Output { 268 | Self(self.0 ^ rhs.0) 269 | } 270 | } 271 | 272 | impl std::ops::BitXorAssign for $newtype { 273 | fn bitxor_assign(&mut self, rhs: Self) { 274 | self.0 ^= rhs.0 275 | } 276 | } 277 | 278 | impl> std::ops::Div for $newtype { 279 | type Output = Self; 280 | 281 | fn div(self, rhs: Self) -> Self::Output { 282 | Self(self.0 / rhs.0) 283 | } 284 | } 285 | 286 | impl std::ops::DivAssign for $newtype { 287 | fn div_assign(&mut self, rhs: Self) { 288 | self.0 /= rhs.0 289 | } 290 | } 291 | 292 | impl> std::ops::Mul for $newtype { 293 | type Output = Self; 294 | 295 | fn mul(self, rhs: Self) -> Self { 296 | Self(self.0 * rhs.0) 297 | } 298 | } 299 | 300 | impl std::ops::MulAssign for $newtype { 301 | fn mul_assign(&mut self, rhs: Self) { 302 | self.0 *= rhs.0 303 | } 304 | } 305 | 306 | impl> std::ops::Not for $newtype { 307 | type Output = Self; 308 | 309 | fn not(self) -> Self::Output { 310 | Self(!self.0) 311 | } 312 | } 313 | 314 | impl> std::ops::Rem for $newtype { 315 | type Output = Self; 316 | 317 | fn rem(self, modulus: Self) -> Self::Output { 318 | Self(self.0 % modulus.0) 319 | } 320 | } 321 | 322 | impl std::ops::RemAssign for $newtype { 323 | fn rem_assign(&mut self, modulus: Self) { 324 | self.0 %= modulus.0; 325 | } 326 | } 327 | 328 | impl> std::ops::Sub for $newtype { 329 | type Output = Self; 330 | 331 | fn sub(self, other: Self) -> Self { 332 | Self(self.0 - other.0) 333 | } 334 | } 335 | 336 | impl std::ops::SubAssign for $newtype { 337 | fn sub_assign(&mut self, other: Self) { 338 | self.0 -= other.0 339 | } 340 | } 341 | 342 | impl> std::ops::Neg for $newtype { 343 | type Output = Self; 344 | 345 | fn neg(self) -> Self::Output { 346 | Self(-self.0) 347 | } 348 | } 349 | 350 | impl> std::ops::Shl for $newtype { 351 | type Output = Self; 352 | 353 | fn shl(self, rhs: Self) -> Self { 354 | Self(self.0 << rhs.0) 355 | } 356 | } 357 | 358 | impl std::ops::ShlAssign for $newtype { 359 | fn shl_assign(&mut self, rhs: Self) { 360 | self.0 <<= rhs.0; 361 | } 362 | } 363 | 364 | impl> std::ops::Shr for $newtype { 365 | type Output = Self; 366 | 367 | fn shr(self, rhs: Self) -> Self { 368 | Self(self.0 >> rhs.0) 369 | } 370 | } 371 | 372 | impl std::ops::ShrAssign for $newtype { 373 | fn shr_assign(&mut self, rhs: Self) { 374 | self.0 >>= rhs.0; 375 | } 376 | } 377 | )* 378 | }; 379 | } 380 | 381 | #[cfg(test)] 382 | mod tests { 383 | use std::collections::HashSet; 384 | 385 | #[test] 386 | fn it_works() { 387 | newtype!(Id, Nested); 388 | 389 | let mut id = Id(0); 390 | let mut id_1 = Id(1); 391 | // Deref 392 | assert_eq!(*id, 0); 393 | //DerefMut 394 | *id = 2; 395 | assert_eq!(*id, 2); 396 | // Add 397 | assert_eq!(id + id_1, Id(3)); 398 | // AddAssign 399 | id += id_1; 400 | assert_eq!(id, Id(3)); 401 | // Clone 402 | let id_2 = id.clone(); 403 | assert_eq!(id, id_2); 404 | // Copy 405 | let id_2 = id; 406 | assert_eq!(id, id_2); 407 | // PartialEq 408 | assert_eq!(id, id); 409 | // Eq 410 | assert_eq!(id, id); 411 | // BitAnd 412 | assert_eq!(Id(1) & Id(2), Id(0)); 413 | // BitAndAssign 414 | id_1 &= Id(2); 415 | assert_eq!(id_1, Id(0)); 416 | // BitOr 417 | assert_eq!(Id(1) | Id(2), Id(3)); 418 | // BitOrAssign 419 | id_1 |= Id(1); 420 | assert_eq!(id_1, Id(1)); 421 | // BitXor 422 | assert_eq!(Id(1) ^ Id(2), Id(3)); 423 | // BitXorAssign 424 | id_1 ^= Id(2); 425 | assert_eq!(id_1, Id(3)); 426 | // Div 427 | assert_eq!(Id(2) / Id(2), Id(1)); 428 | // DivAssign 429 | id_1 /= Id(2); 430 | assert_eq!(id_1, Id(1)); 431 | // Mul 432 | assert_eq!(Id(1) * Id(2), Id(2)); 433 | // MulAssign 434 | id_1 *= Id(2); 435 | assert_eq!(id_1, Id(2)); 436 | // Not 437 | assert_eq!(!Id(0), Id(-1)); 438 | // Ord 439 | assert_eq!(Id(0).cmp(&Id(0)), std::cmp::Ordering::Equal); 440 | // PartialOrd 441 | assert_eq!(Id(0).partial_cmp(&Id(0)), Some(std::cmp::Ordering::Equal)); 442 | // Rem 443 | assert_eq!(Id(2) % Id(2), Id(0)); 444 | // RemAssign 445 | id_1 %= Id(2); 446 | assert_eq!(id_1, Id(0)); 447 | // Sub 448 | assert_eq!(Id(1) - Id(1), Id(0)); 449 | // SubAssign 450 | id_1 -= Id(1); 451 | assert_eq!(id_1, Id(-1)); 452 | // Neg 453 | assert_eq!(-Id(1), Id(-1)); 454 | // Shl 455 | assert_eq!(Id(1) << Id(1), Id(2)); 456 | // ShlAssign 457 | id_1 <<= Id(1); 458 | assert_eq!(id_1, Id(-2)); 459 | // Shr 460 | assert_eq!(Id(1) >> Id(1), Id(0)); 461 | // ShrAssign 462 | id_1 >>= Id(1); 463 | assert_eq!(id_1, Id(-1)); 464 | } 465 | 466 | #[test] 467 | fn nested() { 468 | newtype!(A, B); 469 | 470 | let a = A(B(5)); 471 | 472 | let b: B = 5.into(); 473 | 474 | let b: A> = b.into(); 475 | 476 | assert_eq!(a + b, A(B(10))) 477 | } 478 | 479 | #[test] 480 | fn more_complex() { 481 | newtype!(MySet); 482 | 483 | let mut a = MySet(HashSet::new()); 484 | a.insert(1); 485 | 486 | let mut b = MySet(HashSet::new()); 487 | b.insert(2); 488 | 489 | a.extend(b.iter()); 490 | 491 | assert_eq!(a.len(), 2) 492 | } 493 | } 494 | --------------------------------------------------------------------------------