tuple_combinator/
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
19use 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)]
24pub 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<Self::Tuple>;
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, F: FnOnce(Self::Tuple) -> U>(self, f: F) -> Option<U> {
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<i32>) = (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<i32>) = (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<Self::Tuple>) -> Option<Self::Tuple> {
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<U, F: FnOnce(Self::Tuple) -> Option<U>>(self, f: F) -> Option<U> {
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<P: FnOnce(&Self::Tuple) -> bool>(self, predicate: P) -> Option<Self::Tuple> {
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<Self::Tuple>) -> Option<Self::Tuple> {
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<F: FnOnce() -> Option<Self::Tuple>>(self, f: F) -> Option<Self::Tuple> {
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<Self::Tuple>) -> Option<Self::Tuple> {
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::<Option<i32>>() {
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::<Option<&str>>() {
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)]
190pub 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::<Option<i32>>()
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<U, F: Fn(Option<U>, &dyn Any) -> Option<U>>(&self, init: U, f: F) -> Option<U>;
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<i32>, 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: Any, F: Fn(Option<U>, &U) -> Option<U>>(&self, init: U, f: F) -> Option<U>;
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<i32>, 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::<Option<i32>>().unwrap(), &Some(1));
256    /// assert_eq!(slice[0].downcast_ref::<Option<&str>>(), None);
257    /// assert_eq!(slice[1].downcast_ref::<Option<&str>>().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::<Option<i32>>().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<i32>, Some(()));
275    /// let slice = src.strict_ref_slice::<i32>();
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<i32>].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::<Option<i32>>().unwrap().take();
288    /// ```
289    fn strict_ref_slice<T: Any>(&self) -> Box<[&Option<T>]>;
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<i32>, 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::<Option<i32>>().unwrap(), &Some(1));
307    /// assert_eq!(slice[1].downcast_ref::<Option<&str>>().unwrap(), &None);
308    ///
309    /// let first = slice[0].downcast_mut::<Option<i32>>().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<i32>, Some(()));
325    /// let slice = src.strict_mut_slice::<i32>();
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<i32>].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<T: Any>(&mut self) -> Box<[&mut Option<T>]>;
344}
345
346macro_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<Self::Tuple> {
352                if let ($(Some($v),)*) = self {
353                    Some(($($v,)*))
354                } else {
355                    None
356                }
357            }
358        }
359
360    };
361}
362
363macro_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<U, F: Fn(Option<U>, &dyn Any) -> Option<U>>(&self, init: U, f: F) -> Option<U> {
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: Any, F: Fn(Option<U>, &U) -> Option<U>>(&self, init: U, f: F) -> Option<U> {
383                let mut accu = Some(init);
384
385                $(
386                    let opt = (&self.$nidx as &dyn Any)
387                        .downcast_ref::<Option<U>>()
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<T: Any>(&self) -> Box<[&Option<T>]> {
412                // The maximum amount of elements in a tuple is 12, that's the upper-bound
413                let mut vec: Vec<&Option<T>> = Vec::with_capacity(12);
414
415                $(
416                    (&self.$nidx as &dyn Any)
417                        .downcast_ref::<Option<T>>()
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<T: Any>(&mut self) -> Box<[&mut Option<T>]> {
439                // The maximum amount of elements in a tuple is 12, that's the upper-bound
440                let mut vec: Vec<&mut Option<T>> = Vec::with_capacity(12);
441
442                $(
443                    (&mut self.$nidx as &mut dyn Any)
444                        .downcast_mut::<Option<T>>()
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
458tuple_impls! { t1: T1, }
459tuple_impls! { t1: T1, t2: T2, }
460tuple_impls! { t1: T1, t2: T2, t3: T3, }
461tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, }
462tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, }
463tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, }
464tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, }
465tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, }
466tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, }
467tuple_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
470tuple_impl_reduce! { T0 => 0, }
471tuple_impl_reduce! { T0 => 0, T1 => 1, }
472tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, }
473tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, }
474tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, }
475tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, }
476tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, }
477tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, }
478tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, T8 => 8, }
479tuple_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)]
482mod 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::<Option<i32>>()
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::<Option<i32>>() {
510                    return raw_i32.as_ref().and_then(|val| Some(s + val));
511                }
512
513                if let Some(raw_str) = item.downcast_ref::<Option<&str>>() {
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::<Option<String>>() {
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::<Option<Vec<u8>>>() {
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<i32> = None;
535
536        let res = (Some(1), none, Some(5)).fold(0, |sum, item| {
537            sum.and_then(|s| {
538                item.downcast_ref::<Option<i32>>()
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<i32> = None;
550        let init = 0;
551
552        let res = (Some(1), none, Some(5)).fold(init, |sum, item| {
553            item.downcast_ref::<Option<i32>>()
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<i32>, 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<i32>, 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::<Option<i32>>().unwrap(), &Some(1));
592         assert_eq!(slice[0].downcast_ref::<Option<&str>>(), None);
593         assert_eq!(slice[1].downcast_ref::<Option<&str>>().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<i32>, Some(()));
599        let slice = src.strict_ref_slice::<i32>();
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<i32>].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<i32>, 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::<Option<i32>>().unwrap(), &Some(1));
618        assert_eq!(slice[1].downcast_ref::<Option<&str>>().unwrap(), &None);
619
620        let first = slice[0].downcast_mut::<Option<i32>>().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<i32>, Some(()));
627        let slice = src.strict_mut_slice::<i32>();
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<i32>].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}