├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── lib.rs └── tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /.vscode 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tuple_list" 3 | version = "0.1.3" 4 | authors = ["Valerii Lashmanov "] 5 | edition = "2018" 6 | description = "Crate for macro-free variadic tuple metaprogramming" 7 | documentation = "https://docs.rs/tuple_list" 8 | readme = "README.md" 9 | repository = "https://github.com/VFLashM/tuple_list/" 10 | license = "MIT" 11 | keywords = ["tuple", "variadic", "generic", "meta", "metaprogramming"] 12 | categories = ["data-structures", "rust-patterns"] 13 | 14 | [features] 15 | std = [] 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Valerii Lashmanov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Crate `tuple_list` 2 | 3 | Rust crate for macro-free variadic tuple metaprogramming. 4 | 5 | Provides a way to recursively define traits for 6 | tuples of arbitrary size. 7 | 8 | Please see [crate documentation](https://docs.rs/tuple_list) for details and examples. 9 | 10 | Licensed under MIT license. 11 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] // non-snake case identifiers used in define_tuple_list_traits! for simplicity 2 | #![doc(html_playground_url = "https://play.rust-lang.org/")] 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | 5 | //! Crate for macro-free variadic tuple metaprogramming. 6 | //! 7 | //! # Rationale 8 | //! 9 | //! As of writing this crate, Rust does not support variadic generics 10 | //! and does not allow to reason about tuples in general. 11 | //! 12 | //! Most importantly, Rust does not allow one to generically 13 | //! implement a trait for all tuples whose elements implement it. 14 | //! 15 | //! This crate attempts to fill the gap by providing a way 16 | //! to recursively define traits for tuples. 17 | //! 18 | //! # Tuple lists 19 | //! 20 | //! Tuple `(A, B, C, D)` can be unambiguously mapped into recursive tuple `(A, (B, (C, (D, ()))))`. 21 | //! 22 | //! On each level it consists of a pair `(Head, Tail)`, where `Head` is tuple element and 23 | //! `Tail` is a remainder of the list. For last element `Tail` is an empty list. 24 | //! 25 | //! Unlike regular flat tuples, such recursive tuples can be effectively reasoned about in Rust. 26 | //! 27 | //! This crate calls such structures "tuple lists" and provides a set of traits and macros 28 | //! allowing one to conveniently work with them. 29 | //! 30 | //! # Example 1: `PlusOne` recursive trait 31 | //! 32 | //! Let's create a trait which adds one to each element of a tuple list 33 | //! of arbitrary length, behaving differently depending on element type. 34 | //! 35 | //! ``` 36 | //! // `TupleList` is a helper trait implemented by all tuple lists. 37 | //! // Its use is optional, but it allows to avoid accidentally 38 | //! // implementing traits for something other than tuple lists. 39 | //! use tuple_list::TupleList; 40 | //! 41 | //! // Define trait and implement it for several primitive types. 42 | //! trait PlusOne { 43 | //! fn plus_one(&mut self); 44 | //! } 45 | //! impl PlusOne for i32 { fn plus_one(&mut self) { *self += 1; } } 46 | //! impl PlusOne for bool { fn plus_one(&mut self) { *self = !*self; } } 47 | //! impl PlusOne for String { fn plus_one(&mut self) { self.push('1'); } } 48 | //! 49 | //! // Now we have to implement trait for an empty tuple, 50 | //! // thus defining initial condition. 51 | //! impl PlusOne for () { 52 | //! fn plus_one(&mut self) {} 53 | //! } 54 | //! 55 | //! // Now we can implement trait for a non-empty tuple list, 56 | //! // thus defining recursion and supporting tuple lists of arbitrary length. 57 | //! impl PlusOne for (Head, Tail) where 58 | //! Head: PlusOne, 59 | //! Tail: PlusOne + TupleList, 60 | //! { 61 | //! fn plus_one(&mut self) { 62 | //! self.0.plus_one(); 63 | //! self.1.plus_one(); 64 | //! } 65 | //! } 66 | //! 67 | //! // `tuple_list!` as a helper macro used to create 68 | //! // tuple lists from a list of arguments. 69 | //! use tuple_list::tuple_list; 70 | //! 71 | //! // Now we can use our trait on tuple lists. 72 | //! let mut tuple_list = tuple_list!(2, false, String::from("abc")); 73 | //! tuple_list.plus_one(); 74 | //! 75 | //! // `tuple_list!` macro also allows us to unpack tuple lists 76 | //! let tuple_list!(a, b, c) = tuple_list; 77 | //! assert_eq!(a, 3); 78 | //! assert_eq!(b, true); 79 | //! assert_eq!(&c, "abc1"); 80 | //! ``` 81 | //! 82 | //! # Example 2: `CustomDisplay` recursive trait 83 | //! 84 | //! Let's create a simple `Display`-like trait implemented for all tuples 85 | //! lists whose elements implement it. 86 | //! 87 | //! ``` 88 | //! # use tuple_list::TupleList; 89 | //! # use tuple_list::tuple_list; 90 | //! // Define the trait and implement it for several standard types. 91 | //! trait CustomDisplay { 92 | //! fn fmt(&self) -> String; 93 | //! } 94 | //! impl CustomDisplay for i32 { fn fmt(&self) -> String { self.to_string() } } 95 | //! impl CustomDisplay for bool { fn fmt(&self) -> String { self.to_string() } } 96 | //! impl CustomDisplay for &str { fn fmt(&self) -> String { self.to_string() } } 97 | //! 98 | //! // Now we have to implement trait for an empty tuple, 99 | //! // thus defining initial condition. 100 | //! impl CustomDisplay for () { 101 | //! fn fmt(&self) -> String { String::from("") } 102 | //! } 103 | //! 104 | //! // In order to avoid having trailing spaces, we need 105 | //! // custom logic for tuple lists of exactly one element. 106 | //! // 107 | //! // The simplest way is to use `TupleList::TUPLE_LIST_SIZE` 108 | //! // associated constant, but there is also another option. 109 | //! // 110 | //! // Instead of defining initial condition for empty tuple list 111 | //! // and recursion for non-empty ones, we can define *two* 112 | //! // initial conditions: one for an empty tuple list and 113 | //! // one for tuple lists of exactly one element. 114 | //! // Then we can define recursion for tuple lists of two or more elements. 115 | //! // 116 | //! // Here we define second initial condition for tuple list 117 | //! // of exactly one element. 118 | //! impl CustomDisplay for (Head, ()) where 119 | //! Head: CustomDisplay, 120 | //! { 121 | //! fn fmt(&self) -> String { 122 | //! return self.0.fmt() 123 | //! } 124 | //! } 125 | //! 126 | //! // Recursion step is defined for all tuple lists 127 | //! // longer than one element. 128 | //! impl CustomDisplay for (Head, (Next, Tail)) where 129 | //! Head: CustomDisplay, 130 | //! (Next, Tail): CustomDisplay + TupleList, 131 | //! Tail: TupleList, 132 | //! { 133 | //! fn fmt(&self) -> String { 134 | //! return format!("{} {}", self.0.fmt(), self.1.fmt()); 135 | //! } 136 | //! } 137 | //! 138 | //! // Ensure `fmt` is called for each element. 139 | //! let tuple_list = tuple_list!(2, false, "abc"); 140 | //! assert_eq!( 141 | //! tuple_list.fmt(), 142 | //! "2 false abc", 143 | //! ); 144 | //! 145 | //! // Since tuple lists implement `CustomDisplay` themselves, they can 146 | //! // be elements in other tuple lists implementing `CustomDisplay`. 147 | //! let nested_tuple_list = tuple_list!(2, false, "abc", tuple_list!(3, true, "def")); 148 | //! assert_eq!( 149 | //! nested_tuple_list.fmt(), 150 | //! "2 false abc 3 true def", 151 | //! ); 152 | //! ``` 153 | //! 154 | //! # Example 3: `SwapStringAndInt` recursive trait 155 | //! 156 | //! Let's implement a trait which converts `i32` to `String` and vice versa. 157 | //! 158 | //! This example is way more complex that the other 159 | //! because it maps one tuple list into another tuple list. 160 | //! 161 | //! ``` 162 | //! # use tuple_list::TupleList; 163 | //! # use tuple_list::tuple_list; 164 | //! // Let's define and implement a trait for `i32` and `String` 165 | //! // so that it converts `String` to `i32` and vice versa. 166 | //! trait SwapStringAndInt { 167 | //! type Other; 168 | //! fn swap(self) -> Self::Other; 169 | //! } 170 | //! impl SwapStringAndInt for i32 { 171 | //! type Other = String; 172 | //! fn swap(self) -> String { self.to_string() } 173 | //! } 174 | //! impl SwapStringAndInt for String { 175 | //! type Other = i32; 176 | //! fn swap(self) -> i32 { self.parse().unwrap() } 177 | //! } 178 | //! 179 | //! // Now we have to implement trait for an empty tuple, 180 | //! // thus defining initial condition. 181 | //! impl SwapStringAndInt for () { 182 | //! type Other = (); 183 | //! fn swap(self) -> () { () } 184 | //! } 185 | //! 186 | //! // Now we can implement trait for a non-empty tuple list, 187 | //! // thus defining recursion and supporting tuple lists of arbitrary length. 188 | //! impl SwapStringAndInt for (Head, Tail) where 189 | //! Head: SwapStringAndInt, 190 | //! Tail: SwapStringAndInt + TupleList, 191 | //! TailOther: TupleList, 192 | //! { 193 | //! type Other = (Head::Other, Tail::Other); 194 | //! fn swap(self) -> Self::Other { 195 | //! (self.0.swap(), self.1.swap()) 196 | //! } 197 | //! } 198 | //! 199 | //! // Tuple lists implement `SwapStringAndInt` by calling `SwapStringAndInt::swap` 200 | //! // on each member and returning tuple list of resulting values. 201 | //! let original = tuple_list!(4, String::from("2"), 7, String::from("13")); 202 | //! let swapped = tuple_list!(String::from("4"), 2, String::from("7"), 13); 203 | //! 204 | //! assert_eq!(original.swap(), swapped); 205 | //! ``` 206 | //! 207 | //! # Example 4: prepend and append functions 208 | //! 209 | //! Let's implement append and prepend functions for tuple lists. 210 | //! 211 | //! ``` 212 | //! # use tuple_list::TupleList; 213 | //! # use tuple_list::tuple_list; 214 | //! // Prepend is a trivial operation for tuple lists. 215 | //! // We just create a new pair from prepended element 216 | //! // and the remainder of the list. 217 | //! fn prepend(value: T, tail: Tail) -> (T, Tail) { 218 | //! (value, tail) 219 | //! } 220 | //! 221 | //! // Append is a bit more comples. We'll need a trait for that. 222 | //! trait Append: TupleList { 223 | //! type AppendResult: TupleList; 224 | //! 225 | //! fn append(self, value: T) -> Self::AppendResult; 226 | //! } 227 | //! 228 | //! // Implement append for an empty tuple list. 229 | //! impl Append for () { 230 | //! type AppendResult = (T, ()); 231 | //! 232 | //! // Append for an empty tuple list is quite trivial. 233 | //! fn append(self, value: T) -> Self::AppendResult { (value, ()) } 234 | //! } 235 | //! 236 | //! // Implement append for non-empty tuple list. 237 | //! impl Append for (Head, Tail) where 238 | //! Self: TupleList, 239 | //! Tail: Append, 240 | //! (Head, Tail::AppendResult): TupleList, 241 | //! { 242 | //! type AppendResult = (Head, Tail::AppendResult); 243 | //! 244 | //! // Here we deconstruct tuple list, 245 | //! // recursively call append on the 246 | //! // tail of it, and then reconstruct 247 | //! // the list using the new tail. 248 | //! fn append(self, value: T) -> Self::AppendResult { 249 | //! let (head, tail) = self; 250 | //! return (head, tail.append(value)); 251 | //! } 252 | //! } 253 | //! 254 | //! // Now we can use our append and prepend functions 255 | //! // on tuple lists. 256 | //! let original = tuple_list!( 1, "foo", false); 257 | //! let appended = tuple_list!( 1, "foo", false, 5); 258 | //! let prepended = tuple_list!(5, 1, "foo", false); 259 | //! 260 | //! assert_eq!(original.append(5), appended); 261 | //! assert_eq!(prepend(5, original), prepended); 262 | //! ``` 263 | //! 264 | //! # Example 5: reverse function 265 | //! 266 | //! We can also implement a function which reverses elements of a tuple list. 267 | //! 268 | //! ``` 269 | //! # use tuple_list::TupleList; 270 | //! # use tuple_list::tuple_list; 271 | //! // Rewind is a helper trait which maintains two tuple lists: 272 | //! // `Todo` (which is `Self` for the trait) is the remainder of a tuple list to be reversed. 273 | //! // `Done` is already reversed part of it. 274 | //! trait Rewind { 275 | //! // RewindResult is the type of fully reversed tuple. 276 | //! type RewindResult: TupleList; 277 | //! 278 | //! fn rewind(self, done: Done) -> Self::RewindResult; 279 | //! } 280 | //! 281 | //! // Initial condition. 282 | //! impl Rewind for () { 283 | //! type RewindResult = Done; 284 | //! 285 | //! // When nothing is left to do, just return reversed tuple list. 286 | //! fn rewind(self, done: Done) -> Done { done } 287 | //! } 288 | //! 289 | //! // Recursion step. 290 | //! impl Rewind for (Next, Tail) where 291 | //! Done: TupleList, 292 | //! (Next, Done): TupleList, 293 | //! Tail: Rewind<(Next, Done)> + TupleList, 294 | //! { 295 | //! type RewindResult = Tail::RewindResult; 296 | //! 297 | //! // Strip head element from `Todo` and prepend it to `Done` list, 298 | //! // then recurse on remaining tail of `Todo`. 299 | //! fn rewind(self, done: Done) -> Self::RewindResult { 300 | //! let (next, tail) = self; 301 | //! return tail.rewind((next, done)); 302 | //! } 303 | //! } 304 | //! 305 | //! // Helper function which uses `Rewind` trait to reverse a tuple list. 306 | //! fn reverse(tuple: T) -> T::RewindResult where 307 | //! T: Rewind<()> 308 | //! { 309 | //! // Initial condition, whole tuple list is `Todo`, 310 | //! // empty tuple is `Done`. 311 | //! tuple.rewind(()) 312 | //! } 313 | //! 314 | //! // Now `reverse` is usable on tuple lists. 315 | //! let original = tuple_list!(1, "foo", false); 316 | //! let reversed = tuple_list!(false, "foo", 1); 317 | //! 318 | //! assert_eq!(reverse(original), reversed); 319 | //! ``` 320 | //! 321 | //! # Tuple lists and tuples interoperability 322 | //! 323 | //! This crate defines `Tuple` and `TupleList` traits, which 324 | //! are automatically implemented and allow you to convert 325 | //! tuples into tuple lists and vice versa. 326 | //! 327 | //! Best way to handle interoperability is to store your data 328 | //! as tuple lists and convert them to tuples if necessary. 329 | //! 330 | //! Alternatively it's possible to create a helper function 331 | //! which accepts a tuple, converts it to a tuple list, 332 | //! calls trait method and then returns the result. 333 | //! 334 | //! Here's an example of such function for `Append` 335 | //! trait from previous example: 336 | //! 337 | //! ``` 338 | //! # use tuple_list::TupleList; 339 | //! # use tuple_list::tuple_list; 340 | //! # trait Append: TupleList { 341 | //! # type AppendResult: TupleList; 342 | //! # fn append(self, value: T) -> Self::AppendResult; 343 | //! # } 344 | //! # impl Append for () { 345 | //! # type AppendResult = (T, ()); 346 | //! # fn append(self, value: T) -> Self::AppendResult { (value, ()) } 347 | //! # } 348 | //! # impl Append for (Head, Tail) where 349 | //! # Self: TupleList, 350 | //! # Tail: Append, 351 | //! # (Head, Tail::AppendResult): TupleList, 352 | //! # { 353 | //! # type AppendResult = (Head, Tail::AppendResult); 354 | //! # fn append(self, value: T) -> Self::AppendResult { 355 | //! # let (head, tail) = self; 356 | //! # return (head, tail.append(value)); 357 | //! # } 358 | //! # } 359 | //! // `Tuple` trait is needed to access conversion function. 360 | //! use tuple_list::Tuple; 361 | //! 362 | //! fn append(tuple: T, elem: Elem) -> AppendedTupleList::Tuple where 363 | //! T: Tuple, // input argument tuple 364 | //! T::TupleList: Append, // input argument tuple list 365 | //! AppendedTupleList: TupleList, // resulting tuple list 366 | //! { 367 | //! // Convert tuple into tuple list, append the element 368 | //! // and convert the result back into tuple. 369 | //! tuple.into_tuple_list().append(elem).into_tuple() 370 | //! } 371 | //! 372 | //! // Unlike `Append` trait which is defined for tuple lists, 373 | //! // `append` function works on regular tuples. 374 | //! let original = (1, "foo", false); 375 | //! let appended = (1, "foo", false, 5); 376 | //! 377 | //! assert_eq!(append(original, 5), appended); 378 | //! ``` 379 | //! 380 | //! Please note that tuple/tuple list conversions are 381 | //! destructive and consume the original, which seemingly 382 | //! prevents you from, for example, modifying content 383 | //! of the original tuple. 384 | //! 385 | //! In order to alleviate this problem, `tuple_list` crate 386 | //! introduces `AsTupleOfRefs` trait, which allows one to 387 | //! convert reference to tuple into tuple of references. 388 | //! 389 | //! The idea is that if you you can convert reference to tuple 390 | //! into tuple of references, then convert tuple of references 391 | //! into tuple list and then use recursive trait as usual. 392 | //! 393 | //! Let's modify `PlusOne` trait example so it can be used 394 | //! to modify regular tuples: 395 | //! 396 | //! ``` 397 | //! # use tuple_list::TupleList; 398 | //! # use tuple_list::Tuple; 399 | //! # use tuple_list::AsTupleOfRefs; 400 | //! // Define trait and implement it for several primitive types. 401 | //! trait PlusOne { 402 | //! fn plus_one(&mut self); 403 | //! } 404 | //! impl PlusOne for i32 { fn plus_one(&mut self) { *self += 1; } } 405 | //! impl PlusOne for bool { fn plus_one(&mut self) { *self = !*self; } } 406 | //! impl PlusOne for String { fn plus_one(&mut self) { self.push('1'); } } 407 | //! 408 | //! // Now we have to define a new trait 409 | //! // specifically for tuple lists of references. 410 | //! // 411 | //! // Unlike the original, it accepts `self` by value. 412 | //! trait PlusOneTupleList: TupleList { 413 | //! fn plus_one(self); 414 | //! } 415 | //! 416 | //! // Now we have to implement trait for an empty tuple, 417 | //! // thus defining initial condition. 418 | //! impl PlusOneTupleList for () { 419 | //! fn plus_one(self) {} 420 | //! } 421 | //! 422 | //! // Now we can implement trait for a non-empty tuple list, 423 | //! // thus defining recursion and supporting tuple lists of arbitrary length. 424 | //! // 425 | //! // Note that we're implementing `PlusOneTupleList` for 426 | //! // *tuple list of mutable references*, and as a result 427 | //! // head of the list is a mutable reference, not a value. 428 | //! impl PlusOneTupleList for (&mut Head, Tail) where 429 | //! Self: TupleList, 430 | //! Head: PlusOne, 431 | //! Tail: PlusOneTupleList, 432 | //! { 433 | //! fn plus_one(self) { 434 | //! self.0.plus_one(); 435 | //! self.1.plus_one(); 436 | //! } 437 | //! } 438 | //! 439 | //! // Now let's define a helper function operating on regular tuples. 440 | //! fn plus_one<'a, T, RT>(tuple: &'a mut T) where 441 | //! T: AsTupleOfRefs<'a, TupleOfMutRefs=RT>, 442 | //! RT: Tuple + 'a, 443 | //! RT::TupleList: PlusOneTupleList, 444 | //! 445 | //! { 446 | //! tuple.as_tuple_of_mut_refs().into_tuple_list().plus_one() 447 | //! } 448 | //! 449 | //! // Now we can use this helper function on regular tuples. 450 | //! let mut tuple = (2, false, String::from("abc")); 451 | //! plus_one(&mut tuple); 452 | //! 453 | //! assert_eq!(tuple.0, 3); 454 | //! assert_eq!(tuple.1, true); 455 | //! assert_eq!(&tuple.2, "abc1"); 456 | //! ``` 457 | //! 458 | //! As you can see, working with tuples requires a lot 459 | //! of bolierplate code. Unless you have preexisting code 460 | //! you need to support, it's generally better to use 461 | //! tuple lists directly, since they are much easier 462 | //! to work with. 463 | //! 464 | //! # Implementing recursive traits for regular tuples 465 | //! 466 | //! Implementing recursive traits for regular tuples poses 467 | //! certain problems. As of now it is possible within 468 | //! `tuple_list` crate, but quickly leads to orphan rules 469 | //! violations when used outside of it. 470 | //! 471 | //! You can see a working example of a trait implemented for 472 | //! regular tuples in `tuple_list::test::all_features`, 473 | //! but it's overly complex and pretty much experimental. 474 | //! 475 | //! It should be possible to define recursive traits on regular tuples 476 | //! once trait specialization feature is implemented in Rust. 477 | 478 | /// Trait providing conversion from tuple list into tuple. 479 | /// 480 | /// Generic trait implemented for all tuple lists (up to 12 elements). 481 | /// 482 | /// # Examples 483 | /// 484 | /// ``` 485 | /// use crate::tuple_list::tuple_list; 486 | /// use crate::tuple_list::TupleList; 487 | /// 488 | /// let tuple_list = tuple_list!(1, false, "abc"); 489 | /// 490 | /// assert_eq!( 491 | /// tuple_list.into_tuple(), 492 | /// (1, false, "abc"), 493 | /// ); 494 | /// ``` 495 | pub trait TupleList where Self: Sized { 496 | /// Tuple type corresponding to given tuple list. 497 | type Tuple: Tuple; 498 | 499 | /// Constant representing tuple list size. 500 | const TUPLE_LIST_SIZE: usize; 501 | 502 | /// Converts tuple list into tuple. 503 | fn into_tuple(self) -> Self::Tuple; 504 | } 505 | 506 | /// Trait providing conversion from tuple into tuple list. 507 | /// 508 | /// Generic trait implemented for all tuples (up to 12 elements). 509 | /// 510 | /// Please note that `Tuple` trait does not have 511 | /// `TUPLE_SIZE` constant like `TupleList` does. 512 | /// 513 | /// This is intentional, in order to avoid accidental use of it for tuple lists. 514 | /// 515 | /// You can still get tuple size as `Tuple::TupleList::TUPLE_LIST_SIZE`. 516 | /// 517 | /// # Examples 518 | /// 519 | /// ``` 520 | /// use crate::tuple_list::Tuple; 521 | /// 522 | /// let tuple = (1, false, "abc"); 523 | /// 524 | /// assert_eq!( 525 | /// tuple.into_tuple_list(), 526 | /// (1, (false, ("abc", ()))), 527 | /// ); 528 | /// ``` 529 | pub trait Tuple where Self: Sized { 530 | /// Tuple list type corresponding to given tuple. 531 | type TupleList: TupleList; 532 | 533 | /// Converts tuple into tuple list. 534 | fn into_tuple_list(self) -> Self::TupleList; 535 | } 536 | 537 | /// Trait providing conversion from references to tuples into tuples of references. 538 | /// 539 | /// Generic trait implemented for all tuples (up to 12 elements). 540 | /// 541 | /// # Example 542 | /// ``` 543 | /// use tuple_list::AsTupleOfRefs; 544 | /// 545 | /// fn by_val(tuple: (i32, i32)) {} 546 | /// fn by_ref(tuple: (&i32, &i32)) {} 547 | /// fn by_mut(tuple: (&mut i32, &mut i32)) {} 548 | /// 549 | /// let mut tuple = (1, 2); 550 | /// by_val(tuple); 551 | /// by_ref(tuple.as_tuple_of_refs()); 552 | /// by_mut(tuple.as_tuple_of_mut_refs()); 553 | /// ``` 554 | // TODO: when rust gets generic associated types 555 | // move this trait content into Tuple 556 | pub trait AsTupleOfRefs<'a>: Tuple { 557 | type TupleOfRefs: Tuple + 'a; 558 | type TupleOfMutRefs: Tuple + 'a; 559 | 560 | /// Converts reference to tuple into tuple of references. 561 | fn as_tuple_of_refs(&'a self) -> Self::TupleOfRefs; 562 | 563 | /// Converts mutable reference to tuple into tuple of mutable references. 564 | fn as_tuple_of_mut_refs(&'a mut self) -> Self::TupleOfMutRefs; 565 | } 566 | 567 | /// Trait providing tuple construction function, allows to prepend a value to a tuple. 568 | // TODO: when rust gets generic associated types 569 | // move this trait content into Tuple 570 | pub trait TupleCons: Tuple { 571 | /// Tuple with `Head` prepended to `Self` 572 | type ConsResult: Tuple; 573 | 574 | /// Constructs a tuple from `head` value and `tail` tuple by prepending `head` to `tail`. 575 | /// 576 | /// Reverse of `NonEmptyTuple::uncons`. 577 | /// 578 | /// # Examples 579 | /// 580 | /// ``` 581 | /// use tuple_list::TupleCons; 582 | /// 583 | /// let a = TupleCons::cons("foo", ()); 584 | /// assert_eq!( 585 | /// a, 586 | /// ("foo",), 587 | /// ); 588 | /// 589 | /// let b = TupleCons::cons(false, a); 590 | /// assert_eq!( 591 | /// b, 592 | /// (false, "foo"), 593 | /// ); 594 | /// 595 | /// let c = TupleCons::cons(4, b); 596 | /// assert_eq!( 597 | /// c, 598 | /// (4, false, "foo"), 599 | /// ); 600 | /// ``` 601 | fn cons(head: Head, tail: Self) -> Self::ConsResult; 602 | } 603 | 604 | /// Trait allowing to recursively deconstruct tuples. 605 | /// 606 | /// Generic trait implemented for all non-empty tuples (up to 12 elements). 607 | /// 608 | /// Most interesting part is that this trait allows you to recursively 609 | /// define some simple traits for regular tuples. 610 | /// 611 | /// Unofrtunately, it's not quite complete and is pretty unusable as of now. 612 | /// 613 | /// In order ot be usable outside of this crate it needs support 614 | /// for trait specializations in Rust. 615 | pub trait NonEmptyTuple: Tuple { 616 | /// First element of `Self` tuple. 617 | type Head; 618 | /// Tuple of remaining elements of `Self` tuple. 619 | type Tail: Tuple; 620 | 621 | /// Splits `Self` tuple into head value and tail tuple. 622 | /// 623 | /// Reverse of `TupleCons::cons`. 624 | /// 625 | /// # Examples 626 | /// 627 | /// ``` 628 | /// use tuple_list::NonEmptyTuple; 629 | /// 630 | /// let abcz = (4, false, "foo"); 631 | /// 632 | /// let (a, bcz) = NonEmptyTuple::uncons(abcz); 633 | /// assert_eq!(a, 4); 634 | /// assert_eq!(bcz, (false, "foo")); 635 | /// 636 | /// let (b, cz) = NonEmptyTuple::uncons(bcz); 637 | /// assert_eq!(b, false); 638 | /// assert_eq!(cz, ("foo",)); 639 | /// 640 | /// let (c, z) = NonEmptyTuple::uncons(cz); 641 | /// assert_eq!(c, "foo"); 642 | /// assert_eq!(z, ()); 643 | /// ``` 644 | fn uncons(self) -> (Self::Head, Self::Tail); 645 | 646 | /// Returns first element of a tuple. 647 | /// 648 | /// Same as `NonEmptyTuple::uncons().0`. 649 | fn head(self) -> Self::Head; 650 | 651 | /// Returns all but the first element of a tuple. 652 | /// 653 | /// Same as `NonEmptyTuple::uncons().1`. 654 | fn tail(self) -> Self::Tail; 655 | } 656 | 657 | /// Macro creating tuple list values from list of expressions. 658 | /// 659 | /// # Examples 660 | /// 661 | /// Main use of this macro is to create tuple list values: 662 | /// 663 | /// ``` 664 | /// use tuple_list::tuple_list; 665 | /// 666 | /// let list = tuple_list!(10, false, "foo"); 667 | /// 668 | /// assert_eq!( 669 | /// list, 670 | /// (10, (false, ("foo", ()))), 671 | /// ) 672 | /// ``` 673 | /// 674 | /// Aside from that, `tuple_list!` can sometime be used to define trivial types, 675 | /// but using macro `tuple_list_type!` is recommended instead: 676 | /// 677 | /// ``` 678 | /// # use tuple_list::tuple_list; 679 | /// # use std::collections::HashMap; 680 | /// // Trivial types work just fine with `tuple_list!`. 681 | /// let list: tuple_list_type!(i32, bool, String) = Default::default(); 682 | /// 683 | /// // More complex types will fail when using `tuple_list!`, 684 | /// // but will work with `tuple_list_type!`. 685 | /// use tuple_list::tuple_list_type; 686 | /// 687 | /// let list: tuple_list_type!( 688 | /// &'static str, 689 | /// HashMap, 690 | /// as IntoIterator>::Item, 691 | /// ) = tuple_list!("foo", HashMap::new(), false); 692 | /// ``` 693 | /// 694 | /// It can also be used to unpack tuples: 695 | /// 696 | /// ``` 697 | /// # use tuple_list::tuple_list; 698 | /// let tuple_list!(a, b, c) = tuple_list!(10, false, "foo"); 699 | /// 700 | /// assert_eq!(a, 10); 701 | /// assert_eq!(b, false); 702 | /// assert_eq!(c, "foo"); 703 | /// ``` 704 | /// 705 | /// Unfortunately, due to Rust macro limitations only simple, non-nested match patterns are supported. 706 | #[macro_export] 707 | macro_rules! tuple_list { 708 | () => ( () ); 709 | 710 | // handling simple identifiers, for limited types and patterns support 711 | ($i:ident) => ( ($i, ()) ); 712 | ($i:ident,) => ( ($i, ()) ); 713 | ($i:ident, $($e:ident),*) => ( ($i, $crate::tuple_list!($($e),*)) ); 714 | ($i:ident, $($e:ident),*,) => ( ($i, $crate::tuple_list!($($e),*)) ); 715 | 716 | // handling complex expressions 717 | ($i:expr) => ( ($i, ()) ); 718 | ($i:expr,) => ( ($i, ()) ); 719 | ($i:expr, $($e:expr),*) => ( ($i, $crate::tuple_list!($($e),*)) ); 720 | ($i:expr, $($e:expr),*,) => ( ($i, $crate::tuple_list!($($e),*)) ); 721 | } 722 | 723 | /// Macro creating tuple list types from list of element types. 724 | /// 725 | /// See macro `tuple_list!` for details. 726 | #[macro_export] 727 | macro_rules! tuple_list_type { 728 | () => ( () ); 729 | 730 | ($i:ty) => ( ($i, ()) ); 731 | ($i:ty,) => ( ($i, ()) ); 732 | ($i:ty, $($e:ty),*) => ( ($i, $crate::tuple_list_type!($($e),*)) ); 733 | ($i:ty, $($e:ty),*,) => ( ($i, $crate::tuple_list_type!($($e),*)) ); 734 | } 735 | 736 | // helper, returns first argument, ignores the rest 737 | macro_rules! list_head { 738 | ($i:ident) => ( $i ); 739 | ($i:ident, $($e:ident),+) => ( $i ); 740 | } 741 | 742 | // helper, returns all arguments but the first one 743 | macro_rules! list_tail { 744 | ($i:ident) => ( () ); 745 | ($i:ident, $e:ident) => ( ($e,) ); 746 | ($i:ident, $($e:ident),+) => ( ($($e),*,) ); 747 | } 748 | 749 | // defines Tuple, TupleList, TupleCons, NonEmptyTuple and AsTupleOfRefs 750 | macro_rules! define_tuple_list_traits { 751 | () => ( 752 | impl TupleList for () { 753 | type Tuple = (); 754 | const TUPLE_LIST_SIZE: usize = 0; 755 | fn into_tuple(self) {} 756 | } 757 | impl Tuple for () { 758 | type TupleList = (); 759 | fn into_tuple_list(self) -> () { () } 760 | } 761 | impl<'a> AsTupleOfRefs<'a> for () { 762 | type TupleOfRefs = (); 763 | type TupleOfMutRefs = (); 764 | fn as_tuple_of_refs(&'a self) {} 765 | fn as_tuple_of_mut_refs(&'a mut self) {} 766 | } 767 | ); 768 | ($($x:ident),*) => ( 769 | impl<$($x),*> TupleList for tuple_list_type!($($x),*) { 770 | type Tuple = ($($x),*,); 771 | const TUPLE_LIST_SIZE: usize = ::TupleList::TUPLE_LIST_SIZE + 1; 772 | fn into_tuple(self) -> Self::Tuple { 773 | let tuple_list!($($x),*) = self; 774 | return ($($x),*,) 775 | } 776 | } 777 | impl<$($x),*> Tuple for ($($x),*,) { 778 | type TupleList = tuple_list_type!($($x),*); 779 | fn into_tuple_list(self) -> Self::TupleList { 780 | let ($($x),*,) = self; 781 | return tuple_list!($($x),*); 782 | } 783 | } 784 | impl<'a, $($x: 'a),*> AsTupleOfRefs<'a> for ($($x),*,) { 785 | type TupleOfRefs = ($(&'a $x),*,); 786 | type TupleOfMutRefs = ($(&'a mut $x),*,); 787 | fn as_tuple_of_refs(&'a self) -> Self::TupleOfRefs { 788 | let ($($x),*,) = self; 789 | return ($($x),*,); 790 | } 791 | fn as_tuple_of_mut_refs(&'a mut self) -> Self::TupleOfMutRefs { 792 | let ($($x),*,) = self; 793 | return ($($x),*,); 794 | } 795 | } 796 | impl<$($x),*> NonEmptyTuple for ($($x),*,) { 797 | type Head = list_head!($($x),*); 798 | type Tail = list_tail!($($x),*); 799 | fn uncons(self) -> (Self::Head, Self::Tail) { 800 | let ($($x),*,) = self; 801 | return (list_head!($($x),*), list_tail!($($x),*)); 802 | } 803 | fn head(self) -> Self::Head { self.0 } 804 | fn tail(self) -> Self::Tail { self.uncons().1 } 805 | } 806 | impl<$($x),*> TupleCons for list_tail!($($x),*) { 807 | type ConsResult = ($($x),*,); 808 | fn cons(head: list_head!($($x),*), tail: Self) -> Self::ConsResult { 809 | let list_head!($($x),*) = head; 810 | let list_tail!($($x),*) = tail; 811 | return ($($x),*,); 812 | } 813 | } 814 | ); 815 | } 816 | 817 | // rust only defines common traits for tuples up to 12 elements 818 | // we'll do the same here, increase number if needed 819 | define_tuple_list_traits!(); 820 | define_tuple_list_traits!(T1); 821 | define_tuple_list_traits!(T1, T2); 822 | define_tuple_list_traits!(T1, T2, T3); 823 | define_tuple_list_traits!(T1, T2, T3, T4); 824 | define_tuple_list_traits!(T1, T2, T3, T4, T5); 825 | define_tuple_list_traits!(T1, T2, T3, T4, T5, T6); 826 | define_tuple_list_traits!(T1, T2, T3, T4, T5, T6, T7); 827 | define_tuple_list_traits!(T1, T2, T3, T4, T5, T6, T7, T8); 828 | define_tuple_list_traits!(T1, T2, T3, T4, T5, T6, T7, T8, T9); 829 | define_tuple_list_traits!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); 830 | define_tuple_list_traits!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); 831 | define_tuple_list_traits!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); 832 | 833 | #[cfg(test)] 834 | mod tests; 835 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[cfg(feature = "std")] 4 | #[test] 5 | fn all_features() { 6 | trait NumberOrString { 7 | type OtherType; 8 | 9 | fn into_other(self) -> Self::OtherType; 10 | fn format(&self) -> String; 11 | fn plus_one(&mut self); 12 | } 13 | 14 | impl NumberOrString for i32 { 15 | type OtherType = String; 16 | 17 | fn into_other(self) -> Self::OtherType { self.to_string() } 18 | fn format(&self) -> String { self.to_string() } 19 | fn plus_one(&mut self) { *self += 1; } 20 | } 21 | 22 | impl NumberOrString for String { 23 | type OtherType = i32; 24 | 25 | fn into_other(self) -> Self::OtherType { self.parse().unwrap() } 26 | fn format(&self) -> String { self.clone() } 27 | fn plus_one(&mut self) { self.push('1'); } 28 | } 29 | 30 | // split original trait into separate traits 31 | // for each kind of `self` 32 | trait NumberOrStringTupleListValue: TupleList { 33 | type OtherType: TupleList; 34 | 35 | fn into_other(self) -> Self::OtherType; 36 | } 37 | trait NumberOrStringRef { 38 | fn format_ref(self) -> String; // note that self is by value here 39 | } 40 | trait NumberOrStringMutRef { 41 | fn plus_one_ref(self); // note that self is by value here 42 | } 43 | 44 | // initial condition 45 | impl NumberOrStringTupleListValue for () { 46 | type OtherType = (); 47 | 48 | fn into_other(self) -> Self::OtherType { () } 49 | } 50 | impl NumberOrStringRef for () { 51 | fn format_ref(self) -> String { String::new() } 52 | } 53 | impl NumberOrStringMutRef for () { 54 | fn plus_one_ref(self) {} 55 | } 56 | 57 | // recursion 58 | impl NumberOrStringTupleListValue for (Head, Tail) where 59 | Head: NumberOrString, 60 | Tail: NumberOrStringTupleListValue, 61 | Self: TupleList, 62 | (Head::OtherType, Tail::OtherType): TupleList, 63 | { 64 | type OtherType = (Head::OtherType, Tail::OtherType); 65 | 66 | fn into_other(self) -> Self::OtherType { 67 | (self.0.into_other(), self.1.into_other()) 68 | } 69 | } 70 | 71 | impl NumberOrStringRef for (&Head, Tail) where 72 | Head: NumberOrString, 73 | Tail: NumberOrStringRef + TupleList, 74 | Self: TupleList, 75 | { 76 | fn format_ref(self) -> String { 77 | format!("{} {}", self.0.format(), self.1.format_ref()) 78 | } 79 | } 80 | 81 | impl NumberOrStringMutRef for (&mut Head, Tail) where 82 | Head: NumberOrString, 83 | Tail: NumberOrStringMutRef + TupleList, 84 | Self: TupleList, 85 | { 86 | fn plus_one_ref(self) { 87 | self.0.plus_one(); 88 | self.1.plus_one_ref(); 89 | } 90 | } 91 | 92 | // impl for refs 93 | impl<'a, T, RT> NumberOrStringRef for &'a T where 94 | T: AsTupleOfRefs<'a, TupleOfRefs=RT>, 95 | RT: Tuple + 'a, 96 | RT::TupleList: NumberOrStringRef + TupleList, 97 | { 98 | fn format_ref(self) -> String { 99 | self.as_tuple_of_refs().into_tuple_list().format_ref() 100 | } 101 | } 102 | 103 | impl<'a, T, RT> NumberOrStringMutRef for &'a mut T where 104 | T: AsTupleOfRefs<'a, TupleOfMutRefs=RT>, 105 | RT: Tuple + 'a, 106 | RT::TupleList: NumberOrStringMutRef + TupleList, 107 | { 108 | fn plus_one_ref(self) { 109 | self.as_tuple_of_mut_refs().into_tuple_list().plus_one_ref() 110 | } 111 | } 112 | 113 | 114 | // implementation for tuples 115 | impl NumberOrString for T where 116 | T: Tuple, 117 | T::TupleList: NumberOrStringTupleListValue, 118 | for<'a> &'a T: NumberOrStringRef, 119 | for<'a> &'a mut T: NumberOrStringMutRef, 120 | { 121 | type OtherType = <<::TupleList as NumberOrStringTupleListValue>::OtherType as TupleList>::Tuple; 122 | 123 | fn into_other(self) -> Self::OtherType { 124 | self.into_tuple_list().into_other().into_tuple() 125 | } 126 | fn format(&self) -> String { 127 | self.format_ref() 128 | } 129 | fn plus_one(&mut self) { 130 | self.plus_one_ref() 131 | } 132 | } 133 | 134 | let src = (1, String::from("2"), 3, String::from("4")); 135 | let dst = (String::from("1"), 2, String::from("3"), 4); 136 | assert_eq!( 137 | src.into_other(), 138 | dst, 139 | ); 140 | 141 | let src = (1, String::from("2"), 3, String::from("4")); 142 | assert_eq!( 143 | src.format(), 144 | "1 2 3 4 ", 145 | ); 146 | 147 | let mut src = (1, String::from("2"), 3, String::from("4")); 148 | src.plus_one(); 149 | assert_eq!( 150 | src, 151 | (2, String::from("21"), 4, String::from("41")), 152 | ); 153 | } 154 | 155 | #[test] 156 | #[cfg(feature = "std")] 157 | fn value_single_trait() { 158 | trait NumberOrString { 159 | type OtherType; 160 | 161 | fn into_other(self) -> Self::OtherType; 162 | } 163 | 164 | impl NumberOrString for i32 { 165 | type OtherType = String; 166 | 167 | fn into_other(self) -> Self::OtherType { self.to_string() } 168 | } 169 | 170 | impl NumberOrString for String { 171 | type OtherType = i32; 172 | 173 | fn into_other(self) -> Self::OtherType { self.parse().unwrap() } 174 | } 175 | 176 | // initial condition 177 | impl NumberOrString for () { 178 | type OtherType = (); 179 | 180 | fn into_other(self) -> Self::OtherType { () } 181 | } 182 | 183 | // recursion 184 | impl NumberOrString for (Head, Tail) where 185 | Head: NumberOrString, 186 | Tail: TupleList + NumberOrString, 187 | Self: TupleList, 188 | (Head::OtherType, Tail::OtherType): TupleList, 189 | { 190 | type OtherType = (Head::OtherType, Tail::OtherType); 191 | 192 | fn into_other(self) -> Self::OtherType { 193 | (self.0.into_other(), self.1.into_other()) 194 | } 195 | } 196 | 197 | #[cfg(feature = "std")] 198 | fn into_other(tuple: T) -> OTL::Tuple where 199 | T: Tuple, 200 | T::TupleList: NumberOrString, 201 | OTL: TupleList, 202 | { 203 | tuple.into_tuple_list().into_other().into_tuple() 204 | } 205 | 206 | let src = (1, String::from("2"), 3, String::from("4")); 207 | let dst = (String::from("1"), 2, String::from("3"), 4); 208 | assert_eq!( 209 | into_other(src), 210 | dst, 211 | ); 212 | } 213 | 214 | #[test] 215 | #[cfg(feature = "std")] 216 | fn swap_string_and_int_dual_traits_recursion() { 217 | // real trait, implemented for tuples and primitive types 218 | trait SwapStringAndInt { 219 | type Other; 220 | fn swap(self) -> Self::Other; 221 | } 222 | impl SwapStringAndInt for i32 { 223 | type Other = String; 224 | fn swap(self) -> String { self.to_string() } 225 | } 226 | impl SwapStringAndInt for String { 227 | type Other = i32; 228 | fn swap(self) -> i32 { self.parse().unwrap() } 229 | } 230 | 231 | // helper trait, only used for recursion on tuple lists 232 | // goes to SwapStringAndInt for action conversion 233 | // implemented for tuple lists 234 | trait SwapStringAndIntTupleList { 235 | type Other; 236 | fn swap(self) -> Self::Other; 237 | } 238 | // initial condition for recursion 239 | impl SwapStringAndIntTupleList for () { 240 | type Other = (); 241 | fn swap(self) -> () { () } 242 | } 243 | impl SwapStringAndIntTupleList for (Head, Tail) where 244 | Head: SwapStringAndInt, 245 | Tail: SwapStringAndIntTupleList + TupleList, 246 | Tail::Other: TupleList, 247 | { 248 | type Other = (Head::Other, Tail::Other); 249 | fn swap(self) -> Self::Other { 250 | // note that actual work is done by `SwapStringAndInt` trait 251 | // head is `SwapStringAndInt`, tail is converted to tuple 252 | // which is also `SwapStringAndInt` and then converted back to tuple list 253 | (self.0.swap(), self.1.swap()) 254 | } 255 | } 256 | 257 | // implementation of `SwapStringAndInt` for tuples 258 | impl SwapStringAndInt for T where 259 | T: Tuple, 260 | T::TupleList: SwapStringAndIntTupleList, 261 | OtherTL: TupleList, 262 | { 263 | type Other = OtherTL::Tuple; 264 | fn swap(self) -> Self::Other { 265 | // goes to SwapStringAndIntTupleList for recursion 266 | // converts result back to tuple 267 | self.into_tuple_list().swap().into_tuple() 268 | } 269 | } 270 | 271 | // Create tuple value. 272 | let original = (4, String::from("2"), 7, String::from("13")); 273 | 274 | // Tuples implement `SwapStringAndInt` 275 | let swapped = original.swap(); 276 | assert_eq!( 277 | swapped, 278 | (String::from("4"), 2, String::from("7"), 13), 279 | ); 280 | 281 | // nesting works too 282 | let nested_tuple = ((1, String::from("2"), 3), 4); 283 | let nested_tuple_swapped = nested_tuple.swap(); 284 | assert_eq!( 285 | nested_tuple_swapped, 286 | ((String::from("1"), 2, String::from("3")), String::from("4")), 287 | ); 288 | } 289 | 290 | #[test] 291 | #[cfg(feature = "std")] 292 | fn swap_string_and_int_tuple() { 293 | trait SwapStringAndInt { 294 | type Other; 295 | fn swap(self) -> Self::Other; 296 | } 297 | impl SwapStringAndInt for i32 { 298 | type Other = String; 299 | fn swap(self) -> String { self.to_string() } 300 | } 301 | impl SwapStringAndInt for String { 302 | type Other = i32; 303 | fn swap(self) -> i32 { self.parse().unwrap() } 304 | } 305 | 306 | impl SwapStringAndInt for () { 307 | type Other = (); 308 | fn swap(self) {} 309 | } 310 | 311 | impl SwapStringAndInt for T where 312 | T: NonEmptyTuple, 313 | Head: SwapStringAndInt, 314 | Tail: Tuple + SwapStringAndInt, 315 | TailOther: TupleCons, 316 | { 317 | type Other = TailOther::ConsResult; 318 | fn swap(self) -> Self::Other { 319 | let (head, tail) = self.uncons(); 320 | return TupleCons::cons(head.swap(), tail.swap()); 321 | } 322 | } 323 | 324 | let original = (4, String::from("2"), 7, String::from("13")); 325 | 326 | let swapped = original.swap(); 327 | assert_eq!( 328 | swapped, 329 | (String::from("4"), 2, String::from("7"), 13), 330 | ); 331 | 332 | let nested = ((1, String::from("2")), 3); 333 | let nested_swapped = nested.swap(); 334 | assert_eq!( 335 | nested_swapped, 336 | ((String::from("1"), 2), String::from("3")), 337 | ); 338 | } 339 | 340 | #[test] 341 | #[cfg(feature = "std")] 342 | fn custom_display_tuple() { 343 | // Define trait and implement it for several standard types. 344 | trait CustomDisplay { 345 | fn fmt(self) -> String; 346 | } 347 | impl CustomDisplay for i32 { fn fmt(self) -> String { self.to_string() } } 348 | impl CustomDisplay for bool { fn fmt(self) -> String { self.to_string() } } 349 | impl CustomDisplay for &str { fn fmt(self) -> String { self.to_string() } } 350 | 351 | impl CustomDisplay for () { 352 | fn fmt(self) -> String { String::new() } 353 | } 354 | 355 | impl CustomDisplay for T where 356 | T: NonEmptyTuple, 357 | Head: CustomDisplay, 358 | Tail: CustomDisplay + Tuple, 359 | { 360 | fn fmt(self) -> String { 361 | let (head, tail) = self.uncons(); 362 | return format!("{} {}", head.fmt(), tail.fmt()); 363 | } 364 | } 365 | 366 | let tuple = (2, false, "abc"); 367 | assert_eq!( 368 | tuple.fmt(), 369 | "2 false abc ", 370 | ); 371 | 372 | let recursive_tuple = (2, false, "abc", (3, true, "def")); 373 | assert_eq!( 374 | recursive_tuple.fmt(), 375 | "2 false abc 3 true def ", 376 | ); 377 | } 378 | 379 | #[test] 380 | #[cfg(feature = "std")] 381 | fn plus_one_tuple() { 382 | trait PlusOne<'a> { 383 | fn plus_one(&'a mut self); 384 | } 385 | 386 | impl<'a> PlusOne<'a> for i32 { fn plus_one(&'a mut self) { *self += 1; } } 387 | impl<'a> PlusOne<'a> for bool { fn plus_one(&'a mut self) { *self = !*self; } } 388 | impl<'a> PlusOne<'a> for String { fn plus_one(&'a mut self) { self.push('1'); } } 389 | 390 | trait PlusOneTuple: Tuple { 391 | fn plus_one(self); 392 | } 393 | 394 | impl PlusOneTuple for () { 395 | fn plus_one(self) {} 396 | } 397 | 398 | impl<'a, Head, Tail, T> PlusOneTuple for T where 399 | Head: PlusOne<'a> + 'a, 400 | Tail: PlusOneTuple + 'a, 401 | T: NonEmptyTuple + 'a 402 | { 403 | fn plus_one(self) { 404 | let (head, tail) = self.uncons(); 405 | head.plus_one(); 406 | tail.plus_one(); 407 | } 408 | } 409 | 410 | impl<'a, T, RT> PlusOne<'a> for T where 411 | T: NonEmptyTuple + AsTupleOfRefs<'a, TupleOfMutRefs=RT>, 412 | RT: PlusOneTuple + 'a, 413 | { 414 | fn plus_one(&'a mut self) { 415 | self.as_tuple_of_mut_refs().plus_one() 416 | } 417 | } 418 | 419 | let mut tuple = (2, false, String::from("abc")); 420 | tuple.plus_one(); 421 | let (a, b, c) = tuple; 422 | assert_eq!(a, 3); 423 | assert_eq!(b, true); 424 | assert_eq!(&c, "abc1"); 425 | } 426 | 427 | #[test] 428 | #[cfg(feature = "std")] 429 | fn plus_one_tuple_list_trait_with_lifetime() { 430 | // trait needs free generic lifetime argument 431 | // for implementation to be unambiguous 432 | trait PlusOne<'a> { 433 | fn plus_one(&'a mut self); 434 | } 435 | 436 | // implemented for primitive types 437 | impl<'a> PlusOne<'a> for i32 { fn plus_one(&'a mut self) { *self += 1; } } 438 | impl<'a> PlusOne<'a> for bool { fn plus_one(&'a mut self) { *self = !*self; } } 439 | impl<'a> PlusOne<'a> for String { fn plus_one(&'a mut self) { self.push('1'); } } 440 | 441 | // separate trait for recursion 442 | // implemented for tuple lists 443 | // 444 | // note that `self` is passed by value 445 | // in this case `self` is a tuple list 446 | // of references, which is consumed 447 | trait PlusOneTupleList: TupleList { 448 | fn plus_one(self); 449 | } 450 | 451 | // initial condition 452 | impl PlusOneTupleList for () { 453 | fn plus_one(self) {} 454 | } 455 | 456 | // recursion 457 | impl<'a, Head, Tail> PlusOneTupleList for (&'a mut Head, Tail) where 458 | Head: PlusOne<'a> + 'a, 459 | Tail: PlusOneTupleList + 'a, 460 | Self: TupleList, 461 | { 462 | fn plus_one(self) { 463 | self.0.plus_one(); 464 | self.1.plus_one(); 465 | } 466 | } 467 | 468 | // original trait implementation for regular tuples 469 | impl<'a, T, RT> PlusOne<'a> for T where 470 | T: AsTupleOfRefs<'a, TupleOfMutRefs=RT>, // tuple argument which can be converted into tuple of references 471 | RT: Tuple + 'a, // tuple of references which can be converted into tuple list 472 | RT::TupleList: PlusOneTupleList, // tuple list which implements recursive trait 473 | { 474 | fn plus_one(&'a mut self) { 475 | // 1. converts reference to tuple into tuple of references 476 | // 2. converts tuple of references into tuple list of references 477 | // 3. calls recursive trait on tuple list 478 | self.as_tuple_of_mut_refs().into_tuple_list().plus_one(); 479 | } 480 | } 481 | 482 | // now original trait can be used on regular tuples 483 | // nesting works 484 | let mut tuple = (2, false, String::from("abc")); 485 | tuple.plus_one(); 486 | let (a, b, c) = tuple; 487 | assert_eq!(a, 3); 488 | assert_eq!(b, true); 489 | assert_eq!(&c, "abc1"); 490 | } 491 | 492 | #[test] 493 | #[cfg(feature = "std")] 494 | fn plus_one_tuple_list_trait_without_lifetime() { 495 | // define trait and implement it for primitive types 496 | trait PlusOne { 497 | fn plus_one(&mut self); 498 | } 499 | impl PlusOne for i32 { fn plus_one(&mut self) { *self += 1; } } 500 | impl PlusOne for bool { fn plus_one(&mut self) { *self = !*self; } } 501 | impl PlusOne for String { fn plus_one(&mut self) { self.push('1'); } } 502 | 503 | // separate trait for recursion 504 | // implemented for tuple lists 505 | // 506 | // note that `self` is passed by value 507 | // in this case `self` is a tuple list 508 | // of references, which is consumed 509 | trait PlusOneTupleList: TupleList { 510 | fn plus_one(self); 511 | } 512 | 513 | // initial condition 514 | impl PlusOneTupleList for () { 515 | fn plus_one(self) {} 516 | } 517 | 518 | // recursion 519 | impl<'a, Head, Tail> PlusOneTupleList for (&'a mut Head, Tail) where 520 | Head: PlusOne + 'a, 521 | Tail: PlusOneTupleList + 'a, 522 | Self: TupleList, 523 | { 524 | fn plus_one(self) { 525 | self.0.plus_one(); 526 | self.1.plus_one(); 527 | } 528 | } 529 | 530 | // regular tuples do not implement any traits 531 | // but it's possible to add a helper function which 532 | // will accept tuple and call function of recursive trait 533 | fn plus_one<'a, T, RT>(tuple: &'a mut T) where 534 | T: AsTupleOfRefs<'a, TupleOfMutRefs=RT>, // tuple argument which can be converted into tuple of references 535 | RT: Tuple + 'a, // tuple of references which can be converted into tuple list 536 | RT::TupleList: PlusOneTupleList, // tuple list which implements recursive trait 537 | { 538 | // 1. converts reference to tuple into tuple of references 539 | // 2. converts tuple of references into tuple list of references 540 | // 3. calls recursive trait on tuple list 541 | tuple.as_tuple_of_mut_refs().into_tuple_list().plus_one(); 542 | } 543 | 544 | // helper function can be used to invoke trait function 545 | // on regular tuples 546 | // nesting does not work because tuples don't implement the trait 547 | let mut tuple = (2, false, String::from("abc")); 548 | plus_one(&mut tuple); 549 | let (a, b, c) = tuple; 550 | assert_eq!(a, 3); 551 | assert_eq!(b, true); 552 | assert_eq!(&c, "abc1"); 553 | } 554 | 555 | #[test] 556 | fn empty() { 557 | assert_eq!(().into_tuple_list(), ()); 558 | assert_eq!((), ().into_tuple()); 559 | } 560 | 561 | #[test] 562 | fn single() { 563 | assert_eq!((false,).into_tuple_list(), (false, ())); 564 | assert_eq!((false,), (false, ()).into_tuple()); 565 | } 566 | 567 | #[test] 568 | fn double() { 569 | assert_eq!((false, 1).into_tuple_list(), (false, (1, ()))); 570 | assert_eq!((false, 1), (false, (1, ())).into_tuple()); 571 | } 572 | 573 | #[test] 574 | fn triple() { 575 | assert_eq!((false, 1, "abc").into_tuple_list(), (false, (1, ("abc", ())))); 576 | assert_eq!((false, 1, "abc"), (false, (1, ("abc", ()))).into_tuple()); 577 | } 578 | 579 | #[test] 580 | #[cfg(feature = "std")] 581 | fn complex_types() { 582 | use std::collections::HashMap; 583 | let t : tuple_list_type!(i32, &str, HashMap) = (1, ("abc", (HashMap::new(), ()))); 584 | let tuple_list!(a, b, c) = t; 585 | assert_eq!(a, 1); 586 | assert_eq!(b, "abc"); 587 | assert_eq!(c, HashMap::new()); 588 | } 589 | 590 | #[test] 591 | fn complex_values() { 592 | let s = "abc"; 593 | let t = tuple_list!(s.len(), s, 2 + 3); 594 | let tuple_list!(a, b, c) = t; 595 | assert_eq!(a, 3); 596 | assert_eq!(b, "abc"); 597 | assert_eq!(c, 5); 598 | } 599 | 600 | /* 601 | #[test] 602 | fn complex_unpack() { 603 | let tuple_list!(a, Some(tuple_list!(b, c, d))) = tuple_list!(1, Some(tuple_list!(2, 3, 4))) 604 | assert_eq!(a, 1); 605 | assert_eq!(b, 2); 606 | assert_eq!(c, 3); 607 | assert_eq!(d, 4); 608 | } 609 | */ 610 | 611 | #[test] 612 | fn trailing_comma() { 613 | { // values 614 | let _a = tuple_list!(); 615 | let _b = tuple_list!(0); 616 | let _c = tuple_list!(0,); 617 | let _d = tuple_list!(0,false); 618 | let _e = tuple_list!(0,false,); 619 | } 620 | { // types 621 | let _a : tuple_list_type!() = Default::default(); 622 | let _b : tuple_list_type!(i32) = Default::default(); 623 | let _c : tuple_list_type!(i32,) = Default::default(); 624 | let _d : tuple_list_type!(i32,bool) = Default::default(); 625 | let _e : tuple_list_type!(i32,bool,) = Default::default(); 626 | } 627 | } 628 | 629 | #[test] 630 | #[cfg(feature = "std")] 631 | fn traits() { 632 | // test clone (and eq) 633 | let list : tuple_list_type!(bool, i32, String) = tuple_list!(false, 1, String::from("abc")); 634 | assert_eq!(list.clone(), list); // test clone and eq 635 | 636 | // test copy 637 | fn consume(_: tuple_list_type!(i32, bool)) {} 638 | let copy : tuple_list_type!(i32, bool) = tuple_list!(5, false); 639 | consume(copy); 640 | consume(copy); 641 | 642 | // test debug 643 | assert_eq!(format!("{:?}", tuple_list!(1, false, "abc")), "(1, (false, (\"abc\", ())))"); 644 | 645 | // test default 646 | let default: tuple_list_type!(i32, bool, String) = Default::default(); 647 | assert_eq!(default, tuple_list!(0, false, String::new())); 648 | 649 | // test hash, ensure compiles 650 | use std::hash::Hash; 651 | use std::collections::hash_map::DefaultHasher; 652 | let mut hasher = DefaultHasher::new(); 653 | ().hash(&mut hasher); 654 | tuple_list!(false).hash(&mut hasher); 655 | tuple_list!(false, String::from("abc")).hash(&mut hasher); 656 | 657 | // test ord (and therefore partialrod, eq and partialeq) 658 | assert!(tuple_list!(false) < tuple_list!(true)); 659 | assert!(tuple_list!(1, 2) < tuple_list!(2, 3)); 660 | assert!(tuple_list!(5, 3) > tuple_list!(2, 3)); 661 | assert_eq!(tuple_list!(String::from("foo"), false), tuple_list!(String::from("foo"), false)); 662 | assert_ne!(tuple_list!(String::from("foo"), false), tuple_list!(String::from("foo"), true)); 663 | } 664 | 665 | #[test] 666 | fn tuple_list_size() { 667 | assert_eq!(0, ::TUPLE_LIST_SIZE); 668 | assert_eq!(1, ::TUPLE_LIST_SIZE); 669 | #[cfg(feature = "std")] 670 | assert_eq!(2, ::TUPLE_LIST_SIZE); 671 | #[cfg(feature = "std")] 672 | assert_eq!(3, ::TUPLE_LIST_SIZE); 673 | } --------------------------------------------------------------------------------