├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md ├── LICENSE └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | .idea/ 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | script: 8 | - cargo build --verbose 9 | - cargo test 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tuple-combinator" 3 | version = "0.2.1" 4 | authors = ["Zeyi Fan "] 5 | edition = "2018" 6 | description = "Convenience methods for dealing with Option tuples" 7 | license = "MIT" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tuple-combinator 2 | 3 | [![Build Status](https://travis-ci.org/fanzeyi/tuple-combinator.svg?branch=master)](https://travis-ci.org/fanzeyi/tuple-combinator) 4 | [![Crates.io](https://img.shields.io/crates/v/tuple-combinator)](https://crates.io/crates/tuple-combinator) 5 | [![docs.rs](https://img.shields.io/badge/docs.rs-tuple--combinator-blue)](https://docs.rs/tuple-combinator/) 6 | 7 | This crate provides a helper trait that implements several convenience method for tuples (up to 10-elements tuple). Implementing these methods directly for tuple Options can simplify your code a lot when working with many Options at once. 8 | 9 | ## License 10 | 11 | MIT 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019, Zeyi Fan 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 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A helper trait to improve the ergonomics when working with multiple [`Option`]s. After 2 | //! importing [`TupleCombinator`], you can treat a tuple of `Option`s as one `Option`. 3 | //! 4 | //! # Example 5 | //! 6 | //! ``` 7 | //! use tuple_combinator::TupleCombinator; 8 | //! 9 | //! fn main() { 10 | //! let tuples = (Some(1), Some(2), Some(3)); 11 | //! 12 | //! assert_eq!(tuples.map(|(a,b,c)| a + b + c), Some(6)); 13 | //! assert_eq!(tuples.and_then(|(a,b,c)| Some(a + b - c)), Some(0)); 14 | //! assert_eq!(tuples.transpose(), Some((1,2,3))); 15 | //! assert_eq!((Some(1), None).map(|(a, b): (i32, i32)| 100), None); 16 | //! } 17 | //! ``` 18 | 19 | use std::any::Any; 20 | 21 | /// The traits that provides helper functions for tuples. This trait implementation mirros most of 22 | /// the methods defined in [`Option`]. 23 | #[doc(inline)] 24 | pub trait TupleCombinator: Sized { 25 | type Tuple; 26 | 27 | /// Transposes a tuple of [`Option`]s into an `Option` of tuples. This function returns `None` 28 | /// if any of the `Option` is `None`. 29 | /// ``` 30 | /// # use tuple_combinator::TupleCombinator; 31 | /// let left = (Some("foo"), Some(123)); 32 | /// assert_eq!(left.transpose(), Some(("foo", 123))); 33 | /// ``` 34 | fn transpose(self) -> Option; 35 | 36 | /// See [`Option::map`]. 37 | /// 38 | /// # Examples 39 | /// 40 | /// ``` 41 | /// # use tuple_combinator::TupleCombinator; 42 | /// let tuples = (Some("foo"), Some("bar")); 43 | /// assert_eq!(tuples.map(|(a, b)| format!("{}{}", a, b)).unwrap(), "foobar"); 44 | /// ``` 45 | fn map U>(self, f: F) -> Option { 46 | self.transpose().map(f) 47 | } 48 | 49 | /// See [`Option::expect`]. 50 | /// 51 | /// # Examples 52 | /// 53 | /// ``` 54 | /// # use tuple_combinator::TupleCombinator; 55 | /// let tuples = (Some("foo"), Some(123)); 56 | /// assert_eq!(tuples.expect("should not panic"), ("foo", 123)); 57 | /// ``` 58 | /// 59 | /// ```{.should_panic} 60 | /// # use tuple_combinator::TupleCombinator; 61 | /// let tuples: (_, Option) = (Some("foo"), None); 62 | /// tuples.expect("will panic"); 63 | /// ``` 64 | fn expect(self, msg: &str) -> Self::Tuple { 65 | self.transpose().expect(msg) 66 | } 67 | 68 | /// See [`Option::unwrap`]. 69 | /// ``` 70 | /// # use tuple_combinator::TupleCombinator; 71 | /// let tuples = (Some("foo"), Some(123)); 72 | /// assert_eq!(tuples.unwrap(), ("foo", 123)); 73 | /// ``` 74 | /// 75 | /// This example will panic: 76 | /// 77 | /// ```{.should_panic} 78 | /// # use tuple_combinator::TupleCombinator; 79 | /// let tuples: (_, Option) = (Some("foo"), None); 80 | /// tuples.unwrap(); 81 | /// ``` 82 | fn unwrap(self) -> Self::Tuple { 83 | self.transpose().unwrap() 84 | } 85 | 86 | /// See [`Option::and`]. 87 | /// ``` 88 | /// # use tuple_combinator::TupleCombinator; 89 | /// let left = (Some("foo"), Some(123)); 90 | /// let right = Some(("bar", 456)); 91 | /// assert_eq!(left.and(right), right); 92 | /// 93 | /// let left_none = (None, Some(123)); 94 | /// assert_eq!(left_none.and(right), None); 95 | /// ``` 96 | fn and(self, optb: Option) -> Option { 97 | self.transpose().and(optb) 98 | } 99 | 100 | /// See [`Option::and_then`]. 101 | /// ``` 102 | /// # use tuple_combinator::TupleCombinator; 103 | /// let tuples = (Some("foobar"), Some(123)); 104 | /// assert_eq!(tuples.and_then(|(a, b)| Some(a.len() + b)), Some(129)); 105 | /// 106 | /// assert_eq!(tuples.and_then(|(a, b)| if b % 2 != 1 { Some(b) } else { None }), None); 107 | /// ``` 108 | fn and_then Option>(self, f: F) -> Option { 109 | self.transpose().and_then(f) 110 | } 111 | 112 | /// See [`Option::filter`]. 113 | /// ``` 114 | /// # use tuple_combinator::TupleCombinator; 115 | /// let tuples = (Some("foobar"), Some(123)); 116 | /// assert_eq!(tuples.filter(|(a, b)| b % 2 == 1), Some(("foobar", 123))); 117 | /// assert_eq!(tuples.filter(|(a, b)| b % 2 != 1), None); 118 | /// ``` 119 | fn filter bool>(self, predicate: P) -> Option { 120 | self.transpose().filter(predicate) 121 | } 122 | 123 | /// See [`Option::or`]. 124 | /// ``` 125 | /// # use tuple_combinator::TupleCombinator; 126 | /// let left = (Some("foo"), Some(123)); 127 | /// let right = Some(("bar", 456)); 128 | /// assert_eq!(left.or(right), left.transpose()); 129 | /// 130 | /// let left_none = (None, Some(123)); 131 | /// assert_eq!(left_none.or(right), right); 132 | /// ``` 133 | fn or(self, optb: Option) -> Option { 134 | self.transpose().or(optb) 135 | } 136 | 137 | /// See [`Option::or_else`]. 138 | /// ``` 139 | /// # use tuple_combinator::TupleCombinator; 140 | /// let left = (Some("foo"), Some(123)); 141 | /// let right = Some(("bar", 456)); 142 | /// assert_eq!(left.or_else(|| right), left.transpose()); 143 | /// assert_eq!((None, Some(456)).or_else(|| right), right); 144 | /// ``` 145 | fn or_else Option>(self, f: F) -> Option { 146 | self.transpose().or_else(f) 147 | } 148 | 149 | /// See [`Option::xor`]. 150 | /// ``` 151 | /// # use tuple_combinator::TupleCombinator; 152 | /// let left = (Some("foo"), Some(123)); 153 | /// let right = Some(("bar", 456)); 154 | /// assert_eq!(left.xor(None), left.transpose()); 155 | /// assert_eq!(None.xor(left.transpose()), left.transpose()); 156 | /// assert_eq!(left.xor(right), None); 157 | /// ``` 158 | fn xor(self, optb: Option) -> Option { 159 | self.transpose().xor(optb) 160 | } 161 | } 162 | 163 | /// Reduce tuples of [`Option`]s into results of various form, act in comparable to the iterators. 164 | /// ``` 165 | /// use tuple_combinator::TupleReducer; 166 | /// 167 | /// let res = (Some(1), Some(5), Some("rust_tuple")).fold(0, |sum, item| { 168 | /// sum.and_then(|s| { 169 | /// if let Some(raw_i32) = item.downcast_ref::>() { 170 | /// return raw_i32.as_ref() 171 | /// .and_then(|val| { 172 | /// Some(s + val) 173 | /// }); 174 | /// } 175 | /// 176 | /// if let Some(raw_str) = item.downcast_ref::>() { 177 | /// return raw_str.as_ref() 178 | /// .and_then(|val| { 179 | /// Some(s + val.len() as i32) 180 | /// }); 181 | /// } 182 | /// 183 | /// Some(s) 184 | /// }) 185 | /// }); 186 | /// 187 | /// assert_eq!(res, Some(16)); 188 | /// ``` 189 | #[doc(inline)] 190 | pub trait TupleReducer: Sized { 191 | /// Fold the tuple to obtain a final outcome. Depending on the implementation of the handler 192 | /// function, the fold can behave differently on various option types or values. 193 | /// 194 | /// # Examples 195 | /// 196 | /// Reduce tuples of i32 options to the sum of the contained values: 197 | /// 198 | /// ```rust 199 | /// use tuple_combinator::TupleReducer; 200 | /// 201 | /// let res = (Some(17), Some(20)).fold(5, |sum, item| { 202 | /// sum.and_then(|s| { 203 | /// item.downcast_ref::>() 204 | /// .and_then(|raw| raw.as_ref()) 205 | /// .and_then(|val| { 206 | /// Some(s + val) 207 | /// }) 208 | /// }) 209 | /// }); 210 | /// 211 | /// assert_eq!(res, Some(42)); 212 | /// ``` 213 | fn fold, &dyn Any) -> Option>(&self, init: U, f: F) -> Option; 214 | 215 | /// `fold_strict` works very much like `fold`, except that only options with the same wrapped data 216 | /// type as the output type will be "folded", i.e. invoking the supplied folding function. This 217 | /// function will come into handy when the caller only care about the options in the tuples that 218 | /// match the output type. 219 | /// 220 | /// # Examples 221 | /// ```rust 222 | /// use tuple_combinator::TupleReducer; 223 | /// 224 | /// let res = (Some(40), Some("noise"), None as Option, Some(2)) 225 | /// .fold_strict(0i32, |sum, item| { 226 | /// sum.and_then(|s| { 227 | /// Some(s + item) 228 | /// }) 229 | /// }); 230 | /// 231 | /// assert_eq!(res, Some(42)); 232 | /// ``` 233 | fn fold_strict, &U) -> Option>(&self, init: U, f: F) -> Option; 234 | 235 | /// Convert the tuples to a reference slice, where caller can use native iteration tools. Note 236 | /// that this is re-export of the tuples' internal content, hence the slice can't live longer 237 | /// than the tuple self. 238 | /// 239 | /// # Examples 240 | /// 241 | /// ```rust 242 | /// use std::any::Any; 243 | /// use tuple_combinator::TupleReducer; 244 | /// 245 | /// let mut src = (Some(1), None as Option<&str>, Some(2), None as Option, Some(())); 246 | /// 247 | /// // convert the tuples to a slice of `Any` type 248 | /// let slice: Box<[&dyn Any]> = src.ref_slice(); 249 | /// 250 | /// // the slice has the same amount of elements as in the tuples. 251 | /// assert_eq!(slice.len(), 5); 252 | /// 253 | /// // downcast the element to its actual type; wrong type cast will be rejected with a `None` 254 | /// // output from the API call. 255 | /// assert_eq!(slice[0].downcast_ref::>().unwrap(), &Some(1)); 256 | /// assert_eq!(slice[0].downcast_ref::>(), None); 257 | /// assert_eq!(slice[1].downcast_ref::>().unwrap(), &None); 258 | /// 259 | /// // unlike `mut_slice` API, the line below won't compile even if adding the `mut` keyword 260 | /// // to the `slice` variable, because the source slice is immutable. 261 | /// // let first = slice[0].downcast_mut::>().unwrap().take(); 262 | /// ``` 263 | fn ref_slice(&self) -> Box<[&dyn Any]>; 264 | 265 | /// Convert the tuples to a reference slice which only include options wrapping the data of the 266 | /// desired type [`T`]. Options with types other than the given one will be excluded from the slice. 267 | /// Note that if the slice length is 0, it means the source tuple does not contain elements in 268 | /// options that can be converted to type ['T']. 269 | /// 270 | /// # Examples 271 | /// ```rust 272 | /// use tuple_combinator::TupleReducer; 273 | /// 274 | /// let src = (Some(1), None as Option<&str>, Some(2), None as Option, Some(())); 275 | /// let slice = src.strict_ref_slice::(); 276 | /// 277 | /// // The above variable initiation is equivalent to the following: 278 | /// // let slice: Box<[&i32]> = src.strict_ref_slice(); 279 | /// 280 | /// assert_eq!(slice.len(), 3); 281 | /// assert_eq!( 282 | /// slice, 283 | /// vec![&Some(1), &Some(2), &None as &Option].into_boxed_slice() 284 | /// ); 285 | /// 286 | /// // The line below won't compile because the immutability of the slice. 287 | /// // let first = slice[0].downcast_mut::>().unwrap().take(); 288 | /// ``` 289 | fn strict_ref_slice(&self) -> Box<[&Option]>; 290 | 291 | /// This method works similar to `ref_slice`, except that the members of the slice are mutable, 292 | /// such that it is possible to make updates, or taking ownership from the underlying tuples data. 293 | /// Note that modifying or altering the slice data will also cause the same data in the tuples to 294 | /// be altered. 295 | /// 296 | /// # Examples 297 | /// 298 | /// ```rust 299 | /// use std::any::Any; 300 | /// use tuple_combinator::TupleReducer; 301 | /// 302 | /// let mut src = (Some(1), None as Option<&str>, Some(2), None as Option, Some(())); 303 | /// let slice: Box<[&mut dyn Any]> = src.mut_slice(); 304 | /// 305 | /// assert_eq!(slice.len(), 5); 306 | /// assert_eq!(slice[0].downcast_ref::>().unwrap(), &Some(1)); 307 | /// assert_eq!(slice[1].downcast_ref::>().unwrap(), &None); 308 | /// 309 | /// let first = slice[0].downcast_mut::>().unwrap().take(); 310 | /// assert_eq!(first, Some(1)); 311 | /// ``` 312 | fn mut_slice(&mut self) -> Box<[&mut dyn Any]>; 313 | 314 | /// This method works similar to `strict_ref_slice`, except that the members of the slice are 315 | /// mutable, such that it is possible to make updates, or taking ownership from the underlying 316 | /// tuples data. Note that modifying or altering the slice data will also cause the same data 317 | /// in the tuples to be altered. 318 | /// 319 | /// # Examples 320 | /// 321 | /// ```rust 322 | /// use tuple_combinator::TupleReducer; 323 | /// 324 | /// let mut src = (Some(1), None as Option<&str>, Some(2), None as Option, Some(())); 325 | /// let slice = src.strict_mut_slice::(); 326 | /// 327 | /// // The above variable initiation is equivalent to the following: 328 | /// // let slice: Box<[&mut i32]> = src.strict_mut_slice(); 329 | /// 330 | /// assert_eq!(slice.len(), 3); 331 | /// assert_eq!( 332 | /// slice, 333 | /// vec![&mut Some(1), &mut Some(2), &mut None as &mut Option].into_boxed_slice() 334 | /// ); 335 | /// 336 | /// // Now you can take the wrapped content out of the tuples/slice and operate on the element. 337 | /// // Note that operations on the slice element will take the same effect on the origin tuples, 338 | /// // since slice elements are merely mutable borrows. 339 | /// let first = slice[0].take(); 340 | /// assert_eq!(first, Some(1)); 341 | /// assert_eq!(slice[0], &mut None); 342 | /// ``` 343 | fn strict_mut_slice(&mut self) -> Box<[&mut Option]>; 344 | } 345 | 346 | macro_rules! tuple_impls { 347 | ( $( $v:ident: $T:ident, )* ) => { 348 | impl<$($T,)*> TupleCombinator for ($(Option<$T>,)*) { 349 | type Tuple = ($($T,)*); 350 | 351 | fn transpose(self) -> Option { 352 | if let ($(Some($v),)*) = self { 353 | Some(($($v,)*)) 354 | } else { 355 | None 356 | } 357 | } 358 | } 359 | 360 | }; 361 | } 362 | 363 | macro_rules! tuple_impl_reduce { 364 | () => {}; 365 | 366 | ( $( $ntyp:ident => $nidx:tt, )+ ) => { 367 | 368 | impl<$( $ntyp, )+> TupleReducer for ( $( Option<$ntyp>, )+ ) 369 | where 370 | $( $ntyp: Any, )* 371 | { 372 | fn fold, &dyn Any) -> Option>(&self, init: U, f: F) -> Option { 373 | let mut accu = Some(init); 374 | 375 | $( 376 | accu = f(accu, &self.$nidx); 377 | )* 378 | 379 | accu 380 | } 381 | 382 | fn fold_strict, &U) -> Option>(&self, init: U, f: F) -> Option { 383 | let mut accu = Some(init); 384 | 385 | $( 386 | let opt = (&self.$nidx as &dyn Any) 387 | .downcast_ref::>() 388 | .and_then(|opt| opt.as_ref()); 389 | 390 | // avoid using combinator here since closure will cause `accu` to move and lead 391 | // to all sorts of headache. 392 | if let Some(value) = opt { 393 | accu = f(accu, value); 394 | } 395 | )* 396 | 397 | accu 398 | } 399 | 400 | fn ref_slice(&self) -> Box<[&dyn Any]> { 401 | // The maximum amount of elements in a tuple is 12, that's the upper-bound 402 | let mut vec: Vec<&dyn Any> = Vec::with_capacity(12); 403 | 404 | $( 405 | vec.push(&self.$nidx); 406 | )* 407 | 408 | vec.into_boxed_slice() 409 | } 410 | 411 | fn strict_ref_slice(&self) -> Box<[&Option]> { 412 | // The maximum amount of elements in a tuple is 12, that's the upper-bound 413 | let mut vec: Vec<&Option> = Vec::with_capacity(12); 414 | 415 | $( 416 | (&self.$nidx as &dyn Any) 417 | .downcast_ref::>() 418 | .and_then(|opt| { 419 | vec.push(opt); 420 | Some(()) 421 | }); 422 | )* 423 | 424 | vec.into_boxed_slice() 425 | } 426 | 427 | fn mut_slice(&mut self) -> Box<[&mut dyn Any]> { 428 | // The maximum amount of elements in a tuple is 12, that's the upper-bound 429 | let mut vec: Vec<&mut dyn Any> = Vec::with_capacity(12); 430 | 431 | $( 432 | vec.push(&mut self.$nidx); 433 | )* 434 | 435 | vec.into_boxed_slice() 436 | } 437 | 438 | fn strict_mut_slice(&mut self) -> Box<[&mut Option]> { 439 | // The maximum amount of elements in a tuple is 12, that's the upper-bound 440 | let mut vec: Vec<&mut Option> = Vec::with_capacity(12); 441 | 442 | $( 443 | (&mut self.$nidx as &mut dyn Any) 444 | .downcast_mut::>() 445 | .and_then(|opt| { 446 | vec.push(opt); 447 | Some(()) 448 | }); 449 | )* 450 | 451 | vec.into_boxed_slice() 452 | } 453 | } 454 | }; 455 | } 456 | 457 | // Impl TupleCombinator 458 | tuple_impls! { t1: T1, } 459 | tuple_impls! { t1: T1, t2: T2, } 460 | tuple_impls! { t1: T1, t2: T2, t3: T3, } 461 | tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, } 462 | tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, } 463 | tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, } 464 | tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, } 465 | tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, } 466 | tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, } 467 | tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, t10: T10, } 468 | 469 | // Impl TupleReducer 470 | tuple_impl_reduce! { T0 => 0, } 471 | tuple_impl_reduce! { T0 => 0, T1 => 1, } 472 | tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, } 473 | tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, } 474 | tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, } 475 | tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, } 476 | tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, } 477 | tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, } 478 | tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, T8 => 8, } 479 | tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, T8 => 8, T9 => 9, } 480 | 481 | #[cfg(test)] 482 | mod impl_tests { 483 | use super::TupleReducer; 484 | use std::any::Any; 485 | 486 | #[test] 487 | fn fold_sum() { 488 | let res = (Some(17), Some(20)).fold(5, |sum, item| { 489 | sum.and_then(|s| { 490 | item.downcast_ref::>() 491 | .and_then(|raw| raw.as_ref()) 492 | .and_then(|val| Some(s + val)) 493 | }) 494 | }); 495 | 496 | assert_eq!(res, Some(42)); 497 | } 498 | 499 | #[test] 500 | fn fold_mixed() { 501 | let res = ( 502 | Some(1), 503 | Some(5), 504 | Some("rust_tuple"), 505 | Some(String::from("tuple_reducer")), 506 | Some(vec![0u8, 1, 42]), // the vec that wraps all the wisdom of this universe 507 | ).fold(0, |sum, item| { 508 | sum.and_then(|s| { 509 | if let Some(raw_i32) = item.downcast_ref::>() { 510 | return raw_i32.as_ref().and_then(|val| Some(s + val)); 511 | } 512 | 513 | if let Some(raw_str) = item.downcast_ref::>() { 514 | return raw_str.as_ref().and_then(|val| Some(s + val.len() as i32)); 515 | } 516 | 517 | if let Some(raw_string) = item.downcast_ref::>() { 518 | return raw_string.as_ref().and_then(|val| Some(s + val.len() as i32)); 519 | } 520 | 521 | if let Some(raw_vec) = item.downcast_ref::>>() { 522 | return raw_vec.as_ref().and_then(|val| Some(s + val.len() as i32)); 523 | } 524 | 525 | Some(s) 526 | }) 527 | }); 528 | 529 | assert_eq!(res, Some(32)); 530 | } 531 | 532 | #[test] 533 | fn fold_none_as_nuke() { 534 | let none: Option = None; 535 | 536 | let res = (Some(1), none, Some(5)).fold(0, |sum, item| { 537 | sum.and_then(|s| { 538 | item.downcast_ref::>() 539 | .and_then(|raw| raw.as_ref()) 540 | .and_then(|val| Some(s + val)) 541 | }) 542 | }); 543 | 544 | assert_eq!(res, None); 545 | } 546 | 547 | #[test] 548 | fn fold_none_as_reset() { 549 | let none: Option = None; 550 | let init = 0; 551 | 552 | let res = (Some(1), none, Some(5)).fold(init, |sum, item| { 553 | item.downcast_ref::>() 554 | .and_then(|raw| raw.as_ref()) 555 | .and_then(|val| { 556 | if let Some(s) = sum { 557 | Some(s + val) 558 | } else { 559 | Some(init + val) 560 | } 561 | }) 562 | }); 563 | 564 | assert_eq!(res, Some(5)); 565 | } 566 | 567 | #[test] 568 | fn fold_strict_base() { 569 | let res = (Some(40), Some("noise"), None as Option, Some(2)) 570 | .fold_strict(0i32, |sum, item| { 571 | sum.and_then(|s| { 572 | Some(s + item) 573 | }) 574 | }); 575 | 576 | assert_eq!(res, Some(42)); 577 | } 578 | 579 | #[test] 580 | fn ref_slice_base() { 581 | let src = (Some(1), None as Option<&str>, Some(2), None as Option, Some(())); 582 | 583 | // convert the tuples to a slice of `Any` type 584 | let slice: Box<[&dyn Any]> = src.ref_slice(); 585 | 586 | // the slice has the same amount of elements as in the tuples. 587 | assert_eq!(slice.len(), 5); 588 | 589 | // downcast the element to its actual type; wrong type cast will be rejected with a `None` 590 | // output from the API call. 591 | assert_eq!(slice[0].downcast_ref::>().unwrap(), &Some(1)); 592 | assert_eq!(slice[0].downcast_ref::>(), None); 593 | assert_eq!(slice[1].downcast_ref::>().unwrap(), &None); 594 | } 595 | 596 | #[test] 597 | fn strict_ref_slice_base() { 598 | let src = (Some(1), None as Option<&str>, Some(2), None as Option, Some(())); 599 | let slice = src.strict_ref_slice::(); 600 | 601 | // The above variable initiation is equivalent to the following: 602 | // let slice: Box<[&i32]> = src.strict_ref_slice(); 603 | 604 | assert_eq!(slice.len(), 3); 605 | assert_eq!( 606 | slice, 607 | vec![&Some(1), &Some(2), &None as &Option].into_boxed_slice() 608 | ); 609 | } 610 | 611 | #[test] 612 | fn mut_slice_base() { 613 | let mut src = (Some(1), None as Option<&str>, Some(2), None as Option, Some(())); 614 | let slice: Box<[&mut dyn Any]> = src.mut_slice(); 615 | 616 | assert_eq!(slice.len(), 5); 617 | assert_eq!(slice[0].downcast_ref::>().unwrap(), &Some(1)); 618 | assert_eq!(slice[1].downcast_ref::>().unwrap(), &None); 619 | 620 | let first = slice[0].downcast_mut::>().unwrap().take(); 621 | assert_eq!(first, Some(1)); 622 | } 623 | 624 | #[test] 625 | fn strict_mut_slice_base() { 626 | let mut src = (Some(1), None as Option<&str>, Some(2), None as Option, Some(())); 627 | let slice = src.strict_mut_slice::(); 628 | 629 | // The above variable initiation is equivalent to the following: 630 | // let slice: Box<[&mut i32]> = src.strict_mut_slice(); 631 | 632 | assert_eq!(slice.len(), 3); 633 | assert_eq!( 634 | slice, 635 | vec![&mut Some(1), &mut Some(2), &mut None as &mut Option].into_boxed_slice() 636 | ); 637 | 638 | let first = slice[0].take(); 639 | assert_eq!(first, Some(1)); 640 | assert_eq!(slice[0], &mut None); 641 | } 642 | } 643 | --------------------------------------------------------------------------------