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