Skip to main content

cubecl_core/frontend/
runtime_option.rs

1use cubecl_macros::derive_expand;
2
3use crate as cubecl;
4use crate::prelude::*;
5
6#[derive_expand(CubeType, CubeTypeMut, IntoRuntime)]
7#[cube(runtime_variants, no_constructors)]
8pub enum Option<T: CubeType> {
9    /// No value.
10    None,
11    /// Some value of type `T`.
12    Some(T),
13}
14
15fn discriminant(variant_name: &'static str) -> i32 {
16    OptionExpand::<u32>::discriminant_of(variant_name)
17}
18
19pub enum OptionArgs<T: LaunchArg> {
20    Some(<T as LaunchArg>::RuntimeArg),
21    None,
22}
23
24impl<T: LaunchArg> From<Option<<T as LaunchArg>::RuntimeArg>> for OptionArgs<T> {
25    fn from(value: Option<<T as LaunchArg>::RuntimeArg>) -> Self {
26        match value {
27            Some(arg) => Self::Some(arg),
28            None => Self::None,
29        }
30    }
31}
32
33impl<T: LaunchArg + CubeType + Default + IntoRuntime + 'static> LaunchArg for Option<T> {
34    type RuntimeArg = OptionArgs<T>;
35    type CompilationArg = OptionCompilationArg<T>;
36
37    fn register(arg: Self::RuntimeArg, launcher: &mut KernelLauncher) -> Self::CompilationArg {
38        match arg {
39            OptionArgs::Some(arg) => OptionCompilationArg::Some(T::register(arg, launcher)),
40            OptionArgs::None => OptionCompilationArg::None,
41        }
42    }
43
44    fn expand(
45        arg: &Self::CompilationArg,
46        builder: &mut KernelBuilder,
47    ) -> <Self as CubeType>::ExpandType {
48        match arg {
49            OptionCompilationArg::Some(value) => {
50                let value = T::expand(value, builder);
51                OptionExpand {
52                    discriminant: discriminant("Some").into(),
53                    value,
54                }
55            }
56            OptionCompilationArg::None => OptionExpand {
57                discriminant: discriminant("None").into(),
58                value: T::default().__expand_runtime_method(&builder.scope),
59            },
60        }
61    }
62}
63
64pub enum OptionCompilationArg<T: LaunchArg> {
65    Some(T::CompilationArg),
66    None,
67}
68
69impl<T: LaunchArg> Clone for OptionCompilationArg<T> {
70    fn clone(&self) -> Self {
71        match self {
72            OptionCompilationArg::Some(value) => OptionCompilationArg::Some(value.clone()),
73            OptionCompilationArg::None => OptionCompilationArg::None,
74        }
75    }
76}
77
78impl<T: LaunchArg> PartialEq for OptionCompilationArg<T> {
79    fn eq(&self, other: &Self) -> bool {
80        match (self, other) {
81            (Self::Some(l0), Self::Some(r0)) => l0 == r0,
82            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
83        }
84    }
85}
86
87impl<T: LaunchArg> Eq for OptionCompilationArg<T> {}
88
89impl<T: LaunchArg> core::hash::Hash for OptionCompilationArg<T> {
90    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
91        core::mem::discriminant(self).hash(state);
92        match self {
93            OptionCompilationArg::Some(value) => value.hash(state),
94            OptionCompilationArg::None => {}
95        }
96    }
97}
98
99impl<T: LaunchArg> core::fmt::Debug for OptionCompilationArg<T> {
100    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
101        match self {
102            Self::Some(arg0) => f.debug_tuple("Some").field(arg0).finish(),
103            Self::None => write!(f, "None"),
104        }
105    }
106}
107
108/// Extensions for [`Option`]
109#[allow(non_snake_case)]
110pub trait CubeOption<T: CubeType> {
111    /// Create a new [`Option::Some`] in a kernel
112    fn new_Some(_0: T) -> Option<T> {
113        Option::Some(_0)
114    }
115    fn none_with_default(_0: T) -> Option<T> {
116        Option::None
117    }
118
119    #[doc(hidden)]
120    fn __expand_Some(scope: &Scope, value: T::ExpandType) -> OptionExpand<T> {
121        Self::__expand_new_Some(scope, value)
122    }
123    #[doc(hidden)]
124    fn __expand_new_Some(_scope: &Scope, value: T::ExpandType) -> OptionExpand<T> {
125        OptionExpand::<T> {
126            discriminant: discriminant("Some").into(),
127            value,
128        }
129    }
130    fn __expand_none_with_default(_scope: &Scope, value: T::ExpandType) -> OptionExpand<T> {
131        OptionExpand {
132            discriminant: discriminant("None").into(),
133            value,
134        }
135    }
136}
137
138/// Extensions for [`Option`] that require default
139#[allow(non_snake_case)]
140pub trait CubeOptionDefault<T: CubeType + Default + IntoRuntime>: CubeOption<T> {
141    /// Create a new [`Option::None`] in a kernel
142    fn new_None() -> Option<T> {
143        Option::None
144    }
145
146    #[doc(hidden)]
147    fn __expand_new_None(scope: &Scope) -> OptionExpand<T> {
148        let value = T::default().__expand_runtime_method(scope);
149        Self::__expand_none_with_default(scope, value)
150    }
151}
152
153impl<T: CubeType> CubeOption<T> for Option<T> {}
154impl<T: CubeType + Default + IntoRuntime> CubeOptionDefault<T> for Option<T> {}
155
156mod impls {
157    use core::ops::{Deref, DerefMut};
158
159    use super::*;
160    use crate as cubecl;
161
162    /////////////////////////////////////////////////////////////////////////////
163    // Type implementation
164    /////////////////////////////////////////////////////////////////////////////
165
166    #[doc(hidden)]
167    impl<T: CubeType> OptionExpand<T> {
168        pub fn __expand_is_some_method(&self, scope: &Scope) -> NativeExpand<bool> {
169            self.discriminant
170                .__expand_eq_method(scope, &discriminant("Some").into())
171        }
172
173        pub fn __expand_is_some_and_method(
174            self,
175            scope: &Scope,
176            f: impl FnOnce(&Scope, T::ExpandType) -> NativeExpand<bool>,
177        ) -> NativeExpand<bool> {
178            match_expand_expr(scope, self, discriminant("None"), |_, _| false)
179                .case(scope, discriminant("Some"), |scope, value| f(scope, value))
180                .finish(scope)
181        }
182
183        pub fn __expand_is_none_or_method(
184            self,
185            scope: &Scope,
186            f: impl FnOnce(&Scope, T::ExpandType) -> NativeExpand<bool>,
187        ) -> NativeExpand<bool> {
188            match_expand_expr(scope, self, discriminant("None"), |_, _| true)
189                .case(scope, discriminant("Some"), |scope, value| f(scope, value))
190                .finish(scope)
191        }
192
193        pub fn __expand_expect_method(self, scope: &Scope, msg: &str) -> T::ExpandType
194        where
195            T::ExpandType: RuntimeAssign,
196        {
197            // Replace with `trap` eventually to ensure execution doesn't continue to the next kernel
198            match_expand_expr(scope, self, discriminant("Some"), |_, value| value)
199                .case(scope, discriminant("None"), |scope, value| {
200                    printf_expand(scope, msg, alloc::vec![]);
201                    terminate!();
202                    value
203                })
204                .finish(scope)
205        }
206
207        pub fn __expand_unwrap_or_else_method<F>(self, scope: &Scope, f: F) -> T::ExpandType
208        where
209            F: FnOnce(&Scope) -> T::ExpandType,
210            T::ExpandType: RuntimeAssign,
211        {
212            match_expand_expr(scope, self, discriminant("Some"), |_, value| value)
213                .case(scope, discriminant("None"), |scope, _| f(scope))
214                .finish(scope)
215        }
216
217        pub fn __expand_map_method<U, F>(self, scope: &Scope, f: F) -> OptionExpand<U>
218        where
219            F: FnOnce(&Scope, T::ExpandType) -> U::ExpandType,
220            U: CubeType + IntoRuntime + Default,
221            OptionExpand<U>: RuntimeAssign<Expand = OptionExpand<U>>,
222        {
223            match_expand_expr(scope, self, discriminant("Some"), |scope, value| {
224                let value = f(scope, value);
225                Option::__expand_new_Some(scope, value)
226            })
227            .case(scope, discriminant("None"), |scope, _| {
228                Option::__expand_new_None(scope)
229            })
230            .finish(scope)
231        }
232
233        pub fn __expand_inspect_method<F>(self, scope: &Scope, f: F) -> Self
234        where
235            F: FnOnce(&Scope, &T::ExpandType),
236        {
237            match_expand(
238                scope,
239                self.clone_unchecked(),
240                discriminant("Some"),
241                |scope, value| f(scope, &value),
242            )
243            .case(scope, discriminant("None"), |_, _| {})
244            .finish(scope);
245            self
246        }
247
248        pub fn __expand_map_or_method<U, F>(
249            self,
250            scope: &Scope,
251            default: U::ExpandType,
252            f: F,
253        ) -> U::ExpandType
254        where
255            F: FnOnce(&Scope, T::ExpandType) -> U::ExpandType,
256            U: CubeType + Default + IntoRuntime,
257            U::ExpandType: RuntimeAssign,
258        {
259            match_expand_expr(scope, self, discriminant("Some"), f)
260                .case(scope, discriminant("None"), |_, _| default)
261                .finish(scope)
262        }
263
264        pub fn __expand_map_or_else_method<U, D, F>(
265            self,
266            scope: &Scope,
267            default: D,
268            f: F,
269        ) -> U::ExpandType
270        where
271            D: FnOnce(&Scope) -> U::ExpandType,
272            F: FnOnce(&Scope, T::ExpandType) -> U::ExpandType,
273            U: CubeType + Default + IntoRuntime,
274            U::ExpandType: RuntimeAssign,
275        {
276            match_expand_expr(scope, self, discriminant("Some"), f)
277                .case(scope, discriminant("None"), |scope, _| default(scope))
278                .finish(scope)
279        }
280
281        pub fn __expand_map_or_default_method<U, F>(self, scope: &Scope, f: F) -> U::ExpandType
282        where
283            U: CubeType + IntoRuntime + Default,
284            F: FnOnce(&Scope, T::ExpandType) -> U::ExpandType,
285            U::ExpandType: RuntimeAssign,
286        {
287            match_expand_expr(scope, self, discriminant("Some"), f)
288                .case(scope, discriminant("None"), |scope, _| {
289                    U::default().__expand_runtime_method(scope)
290                })
291                .finish(scope)
292        }
293
294        pub fn __expand_as_deref_method(self, scope: &Scope) -> OptionExpand<T::Target>
295        where
296            T: Deref<Target: CubeType + Default + IntoRuntime>,
297            T::ExpandType: DerefExpand<Target = <T::Target as CubeType>::ExpandType>,
298            <T::Target as CubeType>::ExpandType: RuntimeAssign,
299        {
300            self.__expand_map_method(scope, |scope, value| value.__expand_deref_method(scope))
301        }
302
303        pub fn __expand_as_deref_mut_method(self, scope: &Scope) -> OptionExpand<T::Target>
304        where
305            T: DerefMut<Target: CubeType + Default + IntoRuntime>,
306            T::ExpandType: DerefExpand<Target = <T::Target as CubeType>::ExpandType>,
307            <T::Target as CubeType>::ExpandType: RuntimeAssign,
308        {
309            self.__expand_map_method(scope, |scope, value| value.__expand_deref_method(scope))
310        }
311
312        pub fn __expand_and_then_method<U, F>(self, scope: &Scope, f: F) -> OptionExpand<U>
313        where
314            F: FnOnce(&Scope, T::ExpandType) -> OptionExpand<U>,
315            U: CubeType + IntoRuntime + Default,
316            U::ExpandType: RuntimeAssign,
317        {
318            match_expand_expr(scope, self, discriminant("Some"), f)
319                .case(scope, discriminant("None"), |scope, _| {
320                    Option::__expand_new_None(scope)
321                })
322                .finish(scope)
323        }
324
325        pub fn __expand_filter_method<P>(self, scope: &Scope, predicate: P) -> Self
326        where
327            P: FnOnce(&Scope, &T::ExpandType) -> NativeExpand<bool>,
328            T: Default + IntoRuntime,
329            Self: RuntimeAssign + IntoExpand<Expand = Self>,
330        {
331            match_expand_expr(scope, self, discriminant("Some"), |scope, value| {
332                let cond = predicate(scope, &value);
333                if_else_expr_expand(scope, cond, |scope| Option::__expand_new_Some(scope, value))
334                    .or_else(scope, |scope| Option::__expand_new_None(scope))
335            })
336            .case(scope, discriminant("None"), |scope, _| {
337                Option::__expand_new_None(scope)
338            })
339            .finish(scope)
340        }
341
342        pub fn __expand_or_else_method<F>(self, scope: &Scope, f: F) -> OptionExpand<T>
343        where
344            F: FnOnce(&Scope) -> OptionExpand<T>,
345            OptionExpand<T>: RuntimeAssign + IntoExpand<Expand = OptionExpand<T>>,
346        {
347            let is_some = self.__expand_is_some_method(scope);
348            if_else_expr_expand(scope, is_some, |_| self).or_else(scope, |scope| f(scope))
349        }
350
351        pub fn __expand_zip_with_method<U, F, R>(
352            self,
353            scope: &Scope,
354            other: OptionExpand<U>,
355            f: F,
356        ) -> OptionExpand<R>
357        where
358            F: FnOnce(&Scope, T::ExpandType, U::ExpandType) -> R::ExpandType,
359            U: CubeType,
360            R: CubeType + IntoRuntime + Default,
361            OptionExpand<R>: RuntimeAssign + IntoExpand<Expand = OptionExpand<R>>,
362        {
363            match_expand_expr(scope, self, discriminant("Some"), |scope, value| {
364                match_expand_expr(scope, other, discriminant("Some"), |scope, other| {
365                    let value = f(scope, value, other);
366                    Option::__expand_new_Some(scope, value)
367                })
368                .case(scope, discriminant("None"), |scope, _| {
369                    Option::__expand_new_None(scope)
370                })
371                .finish(scope)
372            })
373            .case(scope, discriminant("None"), |scope, _| {
374                Option::__expand_new_None(scope)
375            })
376            .finish(scope)
377        }
378
379        pub fn __expand_reduce_method<U, R, F>(
380            self,
381            scope: &Scope,
382            other: OptionExpand<U>,
383            f: F,
384        ) -> OptionExpand<R>
385        where
386            T::ExpandType: Into<R::ExpandType>,
387            U::ExpandType: Into<R::ExpandType>,
388            F: FnOnce(&Scope, T::ExpandType, U::ExpandType) -> R::ExpandType,
389            U: CubeType + IntoRuntime + Default,
390            R: CubeType + IntoRuntime + Default,
391            OptionExpand<R>: RuntimeAssign + IntoExpand<Expand = OptionExpand<R>>,
392        {
393            match_expand_expr(scope, self, discriminant("Some"), {
394                let other = other.clone_unchecked();
395                |scope, value| {
396                    match_expand_expr(scope, other, discriminant("Some"), {
397                        let value = value.clone_unchecked();
398                        |scope, other| {
399                            let value = f(scope, value, other);
400                            Option::__expand_new_Some(scope, value)
401                        }
402                    })
403                    .case(scope, discriminant("None"), |scope, _| {
404                        Option::__expand_new_Some(scope, value.into())
405                    })
406                    .finish(scope)
407                }
408            })
409            .case(scope, discriminant("None"), |scope, _| {
410                match_expand_expr(scope, other, discriminant("Some"), |scope, other| {
411                    Option::__expand_new_Some(scope, other.into())
412                })
413                .case(scope, discriminant("None"), |scope, _| {
414                    Option::__expand_new_None(scope)
415                })
416                .finish(scope)
417            })
418            .finish(scope)
419        }
420
421        #[allow(clippy::missing_safety_doc)]
422        pub unsafe fn __expand_unwrap_unchecked_method(self, scope: &Scope) -> T::ExpandType
423        where
424            T::ExpandType: RuntimeAssign,
425        {
426            match_expand_expr(scope, self, discriminant("Some"), |_, value| value).finish(scope)
427        }
428    }
429
430    #[cube(expand_only)]
431    impl<T: CubeType> Option<T> {
432        /// Returns `true` if the option is a [`None`] value.
433        ///
434        /// # Examples
435        ///
436        /// ```
437        /// let x: Option<u32> = Some(2);
438        /// assert_eq!(x.is_none(), false);
439        ///
440        /// let x: Option<u32> = None;
441        /// assert_eq!(x.is_none(), true);
442        /// ```
443        #[must_use = "if you intended to assert that this doesn't have a value, consider \
444                  wrapping this in an `assert!()` instead"]
445        pub fn is_none(&self) -> bool {
446            !self.is_some()
447        }
448
449        /////////////////////////////////////////////////////////////////////////
450        // Getting to contained values
451        /////////////////////////////////////////////////////////////////////////
452
453        /// Returns the contained [`Some`] value, consuming the `self` value.
454        ///
455        /// Because this function may panic, its use is generally discouraged.
456        /// Panics are meant for unrecoverable errors, and
457        /// [may abort the entire program][panic-abort].
458        ///
459        /// Instead, prefer to use pattern matching and handle the [`None`]
460        /// case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], or
461        /// [`unwrap_or_default`]. In functions returning `Option`, you can use
462        /// [the `?` (try) operator][try-option].
463        ///
464        /// [panic-abort]: https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html
465        /// [try-option]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#where-the--operator-can-be-used
466        /// [`unwrap_or`]: Option::unwrap_or
467        /// [`unwrap_or_else`]: Option::unwrap_or_else
468        /// [`unwrap_or_default`]: Option::unwrap_or_default
469        ///
470        /// # Panics
471        ///
472        /// Panics if the self value equals [`None`].
473        ///
474        /// # Examples
475        ///
476        /// ```
477        /// let x = Some("air");
478        /// assert_eq!(x.unwrap(), "air");
479        /// ```
480        ///
481        /// ```should_panic
482        /// let x: Option<&str> = None;
483        /// assert_eq!(x.unwrap(), "air"); // fails
484        /// ```
485        pub fn unwrap(self) -> T
486        where
487            T::ExpandType: RuntimeAssign,
488        {
489            self.expect("called `Option::unwrap()` on a `None` value")
490        }
491
492        /// Returns the contained [`Some`] value or a provided default.
493        ///
494        /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing
495        /// the result of a function call, it is recommended to use [`unwrap_or_else`],
496        /// which is lazily evaluated.
497        ///
498        /// [`unwrap_or_else`]: Option::unwrap_or_else
499        ///
500        /// # Examples
501        ///
502        /// ```
503        /// assert_eq!(Some("car").unwrap_or("bike"), "car");
504        /// assert_eq!(None.unwrap_or("bike"), "bike");
505        /// ```
506        pub fn unwrap_or(self, default: T) -> T
507        where
508            T::ExpandType: RuntimeAssign,
509        {
510            match self {
511                Some(x) => x,
512                None => default,
513            }
514        }
515
516        /// Returns the contained [`Some`] value or a default.
517        ///
518        /// Consumes the `self` argument then, if [`Some`], returns the contained
519        /// value, otherwise if [`None`], returns the [default value] for that
520        /// type.
521        ///
522        /// # Examples
523        ///
524        /// ```
525        /// let x: Option<u32> = None;
526        /// let y: Option<u32> = Some(12);
527        ///
528        /// assert_eq!(x.unwrap_or_default(), 0);
529        /// assert_eq!(y.unwrap_or_default(), 12);
530        /// ```
531        ///
532        /// [default value]: Default::default
533        /// [`parse`]: str::parse
534        /// [`FromStr`]: crate::str::FromStr
535        pub fn unwrap_or_default(self) -> T
536        where
537            T: Default + IntoRuntime,
538            T::ExpandType: RuntimeAssign,
539        {
540            match self {
541                Some(x) => x,
542                None => comptime![T::default()].runtime(),
543            }
544        }
545
546        /////////////////////////////////////////////////////////////////////////
547        // Transforming contained values
548        /////////////////////////////////////////////////////////////////////////
549
550        /////////////////////////////////////////////////////////////////////////
551        // Boolean operations on the values, eager and lazy
552        /////////////////////////////////////////////////////////////////////////
553
554        /// Returns [`None`] if the option is [`None`], otherwise returns `optb`.
555        ///
556        /// Arguments passed to `and` are eagerly evaluated; if you are passing the
557        /// result of a function call, it is recommended to use [`and_then`], which is
558        /// lazily evaluated.
559        ///
560        /// [`and_then`]: Option::and_then
561        ///
562        /// # Examples
563        ///
564        /// ```
565        /// let x = Some(2);
566        /// let y: Option<&str> = None;
567        /// assert_eq!(x.and(y), None);
568        ///
569        /// let x: Option<u32> = None;
570        /// let y = Some("foo");
571        /// assert_eq!(x.and(y), None);
572        ///
573        /// let x = Some(2);
574        /// let y = Some("foo");
575        /// assert_eq!(x.and(y), Some("foo"));
576        ///
577        /// let x: Option<u32> = None;
578        /// let y: Option<&str> = None;
579        /// assert_eq!(x.and(y), None);
580        /// ```
581        pub fn and<U>(self, optb: Option<U>) -> Option<U>
582        where
583            U: CubeType + IntoRuntime + Default,
584            U::ExpandType: RuntimeAssign,
585        {
586            match self {
587                Option::Some(_) => optb,
588                Option::None => Option::new_None(),
589            }
590        }
591
592        /// Returns the option if it contains a value, otherwise returns `optb`.
593        ///
594        /// Arguments passed to `or` are eagerly evaluated; if you are passing the
595        /// result of a function call, it is recommended to use [`or_else`], which is
596        /// lazily evaluated.
597        ///
598        /// [`or_else`]: Option::or_else
599        ///
600        /// # Examples
601        ///
602        /// ```
603        /// let x = Some(2);
604        /// let y = None;
605        /// assert_eq!(x.or(y), Some(2));
606        ///
607        /// let x = None;
608        /// let y = Some(100);
609        /// assert_eq!(x.or(y), Some(100));
610        ///
611        /// let x = Some(2);
612        /// let y = Some(100);
613        /// assert_eq!(x.or(y), Some(2));
614        ///
615        /// let x: Option<u32> = None;
616        /// let y = None;
617        /// assert_eq!(x.or(y), None);
618        /// ```
619        pub fn or(self, optb: Option<T>) -> Option<T>
620        where
621            T::ExpandType: RuntimeAssign,
622        {
623            if self.is_some() { self } else { optb }
624        }
625
626        /// Returns [`Some`] if exactly one of `self`, `optb` is [`Some`], otherwise returns [`None`].
627        ///
628        /// # Examples
629        ///
630        /// ```
631        /// let x = Some(2);
632        /// let y: Option<u32> = None;
633        /// assert_eq!(x.xor(y), Some(2));
634        ///
635        /// let x: Option<u32> = None;
636        /// let y = Some(2);
637        /// assert_eq!(x.xor(y), Some(2));
638        ///
639        /// let x = Some(2);
640        /// let y = Some(2);
641        /// assert_eq!(x.xor(y), None);
642        ///
643        /// let x: Option<u32> = None;
644        /// let y: Option<u32> = None;
645        /// assert_eq!(x.xor(y), None);
646        /// ```
647        pub fn xor(self, optb: Option<T>) -> Option<T>
648        where
649            T: Default + IntoRuntime,
650            T::ExpandType: RuntimeAssign,
651        {
652            let this_is_none = self.is_none();
653
654            if self.is_some() && optb.is_none() {
655                self
656            } else if this_is_none && optb.is_some() {
657                optb
658            } else {
659                Option::new_None()
660            }
661        }
662
663        /////////////////////////////////////////////////////////////////////////
664        // Misc
665        /////////////////////////////////////////////////////////////////////////
666
667        // TODO: `take`/`take_if`/`replace`
668
669        /// Zips `self` with another `Option`.
670        ///
671        /// If `self` is `Some(s)` and `other` is `Some(o)`, this method returns `Some((s, o))`.
672        /// Otherwise, `None` is returned.
673        ///
674        /// # Examples
675        ///
676        /// ```
677        /// let x = Some(1);
678        /// let y = Some("hi");
679        /// let z = None::<u8>;
680        ///
681        /// assert_eq!(x.zip(y), Some((1, "hi")));
682        /// assert_eq!(x.zip(z), None);
683        /// ```
684        pub fn zip<U>(self, other: Option<U>) -> Option<(T, U)>
685        where
686            U: CubeType,
687            (T, U): Default + CubeType + IntoRuntime,
688            (T::ExpandType, U::ExpandType): Into<<(T, U) as CubeType>::ExpandType>,
689            OptionExpand<(T, U)>: RuntimeAssign + IntoExpand<Expand = OptionExpand<(T, U)>>,
690        {
691            match self {
692                Some(a) => match other {
693                    Some(b) => Option::Some((a, b)),
694                    None => Option::new_None(),
695                },
696                None => Option::new_None(),
697            }
698        }
699    }
700
701    #[cube(expand_only)]
702    impl<
703        T: CubeType<ExpandType: RuntimeAssign> + IntoRuntime + Default,
704        U: CubeType<ExpandType: RuntimeAssign> + IntoRuntime + Default,
705    > Option<(T, U)>
706    {
707        /// Unzips an option containing a tuple of two options.
708        ///
709        /// If `self` is `Some((a, b))` this method returns `(Some(a), Some(b))`.
710        /// Otherwise, `(None, None)` is returned.
711        ///
712        /// # Examples
713        ///
714        /// ```
715        /// let x = Some((1, "hi"));
716        /// let y = None::<(u8, u32)>;
717        ///
718        /// assert_eq!(x.unzip(), (Some(1), Some("hi")));
719        /// assert_eq!(y.unzip(), (None, None));
720        /// ```
721        #[inline]
722        pub fn unzip(self) -> (Option<T>, Option<U>) {
723            match self {
724                Option::Some(value) => (Option::Some(value.0), Option::Some(value.1)),
725                Option::None => (Option::new_None(), Option::new_None()),
726            }
727        }
728    }
729}