11use crate :: { Choice , CtEq , CtSelect } ;
22use core:: ops:: { Deref , DerefMut } ;
33
4+ /// Helper macro for providing behavior like the [`CtOption::map`] combinator that works in
5+ /// `const fn` contexts.
6+ ///
7+ /// Requires a provided `$mapper` function to convert from one type to another, e.g.
8+ ///
9+ /// ```ignore
10+ /// const fn mapper(value: T) -> U
11+ /// ```
12+ #[ macro_export]
13+ macro_rules! map {
14+ ( $opt: expr, $mapper: path) => { { $crate:: CtOption :: new( $mapper( $opt. to_inner_unchecked( ) ) , $opt. is_some( ) ) } } ;
15+ }
16+
17+ /// Helper macro for providing behavior like the [`CtOption::unwrap_or`] combinator that works in
18+ /// `const fn` contexts.
19+ ///
20+ /// Requires a provided selector function `$select` to perform constant-time selection which takes
21+ /// two `T` values by reference along with a [`Choice`], returning the first `T` for
22+ /// [`Choice::FALSE`], and the second for [`Choice::TRUE`], e.g.:
23+ ///
24+ /// ```ignore
25+ /// const fn ct_select(a: &T, b: &T, condition: Choice) -> T
26+ /// ```
27+ #[ macro_export]
28+ macro_rules! unwrap_or {
29+ ( $opt: expr, $default: expr, $select: path) => {
30+ $select( & $default, $opt. as_inner_unchecked( ) , $opt. is_some( ) )
31+ } ;
32+ }
33+
434/// Equivalent of [`Option`] but predicated on a [`Choice`] with combinators that allow for
535/// constant-time operations which always perform the same sequence of instructions regardless of
636/// the value of `is_some`.
@@ -23,6 +53,21 @@ impl<T> CtOption<T> {
2353 Self { value, is_some }
2454 }
2555
56+ /// Construct a new [`CtOption`] where `self.is_some()` is [`Choice::TRUE`].
57+ #[ inline]
58+ pub const fn some ( value : T ) -> CtOption < T > {
59+ Self :: new ( value, Choice :: TRUE )
60+ }
61+
62+ /// Construct a new [`CtOption`] with the [`Default`] value, and where `self.is_some()` is
63+ /// [`Choice::FALSE`].
64+ pub fn none ( ) -> CtOption < T >
65+ where
66+ T : Default ,
67+ {
68+ Self :: new ( Default :: default ( ) , Choice :: FALSE )
69+ }
70+
2671 /// Convert from a `&mut CtOption<T>` to `CtOption<&mut T>`.
2772 #[ inline]
2873 pub const fn as_mut ( & mut self ) -> CtOption < & mut T > {
@@ -80,7 +125,7 @@ impl<T> CtOption<T> {
80125 // (needs `const_precise_live_drops`)
81126 #[ inline]
82127 #[ track_caller]
83- pub const fn expect_copied ( & self , msg : & str ) -> T
128+ pub const fn expect_copied ( self , msg : & str ) -> T
84129 where
85130 T : Copy ,
86131 {
@@ -99,7 +144,7 @@ impl<T> CtOption<T> {
99144 pub const fn expect_ref ( & self , msg : & str ) -> & T {
100145 // TODO(tarcieri): use `self.is_some().to_bool()` when MSRV is 1.86
101146 assert ! ( self . is_some. to_bool_vartime( ) , "{}" , msg) ;
102- & self . value
147+ self . as_inner_unchecked ( )
103148 }
104149
105150 /// Convert the [`CtOption`] wrapper into an [`Option`], depending on whether
@@ -124,6 +169,29 @@ impl<T> CtOption<T> {
124169 }
125170 }
126171
172+ /// Convert the [`CtOption`] wrapper into an [`Option`] in a `const fn`-friendly manner.
173+ ///
174+ /// This is the equivalent of [`CtOption::into_option`] but is `const fn`-friendly by only
175+ /// allowing `Copy` types which are implicitly `!Drop` and don't run into problems with
176+ /// `const fn` and destructors.
177+ ///
178+ /// <div class="warning">
179+ /// This implementation doesn't intend to be constant-time nor try to protect the leakage of the
180+ /// `T` value since the [`Option`] will do it anyway.
181+ /// </div>
182+ #[ inline]
183+ pub const fn into_option_copied ( self ) -> Option < T >
184+ where
185+ T : Copy ,
186+ {
187+ // TODO(tarcieri): use `self.is_some().to_bool()` when MSRV is 1.86
188+ if self . is_some . to_bool_vartime ( ) {
189+ Some ( self . value )
190+ } else {
191+ None
192+ }
193+ }
194+
127195 /// Returns [`Choice::TRUE`] if the option is the equivalent of a `Some`.
128196 #[ inline]
129197 #[ must_use]
@@ -164,6 +232,24 @@ impl<T> CtOption<T> {
164232 ret
165233 }
166234
235+ /// Obtain a reference to the inner value without first checking that `self.is_some()` is
236+ /// [`Choice::TRUE`].
237+ ///
238+ /// This method is primarily intended for use in `const fn` scenarios where it's not yet
239+ /// possible to use the safe combinator methods, and returns a reference to avoid issues with
240+ /// `const fn` destructors.
241+ ///
242+ /// <div class="warning">
243+ /// <b>Use with care!</b>
244+ ///
245+ /// This method does not ensure the `value` is actually valid. Callers of this method should
246+ /// take great care to ensure that `self.is_some()` is checked elsewhere.
247+ /// </div>
248+ #[ inline]
249+ pub const fn as_inner_unchecked ( & self ) -> & T {
250+ & self . value
251+ }
252+
167253 /// Calls the provided callback with the wrapped inner value, which computes a [`Choice`],
168254 /// and updates `self.is_some()`.
169255 ///
@@ -178,6 +264,13 @@ impl<T> CtOption<T> {
178264 self
179265 }
180266
267+ /// Apply an additional [`Choice`] requirement to `is_some`.
268+ #[ inline]
269+ pub const fn filter_by ( mut self , is_some : Choice ) -> Self {
270+ self . is_some = self . is_some . and ( is_some) ;
271+ self
272+ }
273+
181274 /// Maps a `CtOption<T>` to a `CtOption<U>` by unconditionally applying a function to the
182275 /// contained `value`, but returning a new option value which inherits `self.is_some()`.
183276 #[ inline]
@@ -247,6 +340,27 @@ impl<T> CtOption<T> {
247340 }
248341 }
249342
343+ /// Obtain a copy of the inner value without first checking that `self.is_some()` is
344+ /// [`Choice::TRUE`].
345+ ///
346+ /// This method is primarily intended for use in `const fn` scenarios where it's not yet
347+ /// possible to use the safe combinator methods, and uses a `Copy` bound to avoid issues with
348+ /// `const fn` destructors.
349+ ///
350+ /// <div class="warning">
351+ /// <b>Use with care!</b>
352+ ///
353+ /// This method does not ensure the `value` is actually valid. Callers of this method should
354+ /// take great care to ensure that `self.is_some()` is checked elsewhere.
355+ /// </div>
356+ #[ inline]
357+ pub const fn to_inner_unchecked ( self ) -> T
358+ where
359+ T : Copy ,
360+ {
361+ self . value
362+ }
363+
250364 /// Return the contained value, consuming the `self` value.
251365 ///
252366 /// Use of this function is discouraged due to panic potential. Instead, prefer non-panicking
@@ -400,6 +514,12 @@ impl<T: CtSelect> CtSelect for CtOption<T> {
400514 }
401515}
402516
517+ impl < T : Default > Default for CtOption < T > {
518+ fn default ( ) -> Self {
519+ Self :: none ( )
520+ }
521+ }
522+
403523/// Convert the [`CtOption`] wrapper into an [`Option`], depending on whether
404524/// [`CtOption::is_some`] is a truthy or falsy [`Choice`].
405525///
@@ -460,6 +580,26 @@ mod tests {
460580 #[ derive( Debug , Eq , PartialEq ) ]
461581 struct Error ;
462582
583+ #[ test]
584+ fn map_macro ( ) {
585+ assert ! ( map!( NONE , u16 :: from) . is_none( ) . to_bool( ) ) ;
586+ assert_eq ! ( map!( SOME , u16 :: from) . unwrap( ) , VALUE as u16 ) ;
587+ }
588+
589+ #[ test]
590+ fn unwrap_or_macro ( ) {
591+ // Don't actually use this! It's just a test function implemented in variable-time
592+ const fn select_vartime ( a : & u8 , b : & u8 , choice : Choice ) -> u8 {
593+ if choice. to_bool_vartime ( ) { * b } else { * a }
594+ }
595+
596+ assert_eq ! (
597+ unwrap_or!( NONE , OTHER . unwrap( ) , select_vartime) ,
598+ OTHER . unwrap( )
599+ ) ;
600+ assert_eq ! ( unwrap_or!( SOME , OTHER . unwrap( ) , select_vartime) , VALUE ) ;
601+ }
602+
463603 #[ test]
464604 fn ct_eq ( ) {
465605 assert ! ( NONE . ct_eq( & NONE ) . to_bool( ) ) ;
@@ -477,6 +617,11 @@ mod tests {
477617 assert ! ( SOME . ct_select( & NONE , Choice :: TRUE ) . is_none( ) . to_bool( ) ) ;
478618 }
479619
620+ #[ test]
621+ fn default ( ) {
622+ assert ! ( NONE . ct_eq( & CtOption :: default ( ) ) . to_bool( ) ) ;
623+ }
624+
480625 #[ test]
481626 fn expect_some ( ) {
482627 assert_eq ! ( SOME . expect( "should succeed" ) , VALUE ) ;
@@ -494,6 +639,12 @@ mod tests {
494639 assert_eq ! ( NONE . into_option( ) , None )
495640 }
496641
642+ #[ test]
643+ fn into_option_copied ( ) {
644+ assert_eq ! ( SOME . into_option_copied( ) , Some ( VALUE ) ) ;
645+ assert_eq ! ( NONE . into_option_copied( ) , None )
646+ }
647+
497648 #[ test]
498649 fn is_some ( ) {
499650 assert ! ( SOME . is_some( ) . to_bool( ) ) ;
@@ -537,6 +688,14 @@ mod tests {
537688 assert_eq ! ( ret. unwrap( ) , VALUE ) ;
538689 }
539690
691+ #[ test]
692+ fn filter_by ( ) {
693+ assert ! ( NONE . filter_by( Choice :: FALSE ) . is_none( ) . to_bool( ) ) ;
694+ assert ! ( NONE . filter_by( Choice :: TRUE ) . is_none( ) . to_bool( ) ) ;
695+ assert ! ( SOME . filter_by( Choice :: FALSE ) . ct_eq( & NONE ) . to_bool( ) ) ;
696+ assert_eq ! ( SOME . filter_by( Choice :: TRUE ) . unwrap( ) , VALUE ) ;
697+ }
698+
540699 #[ test]
541700 fn map ( ) {
542701 assert ! ( NONE . map( |value| value + 1 ) . ct_eq( & NONE ) . to_bool( ) ) ;
@@ -576,6 +735,11 @@ mod tests {
576735 assert ! ( SOME . or( OTHER ) . ct_eq( & SOME ) . to_bool( ) ) ;
577736 }
578737
738+ #[ test]
739+ fn some ( ) {
740+ assert ! ( CtOption :: some( VALUE ) . ct_eq( & SOME ) . to_bool( ) ) ;
741+ }
742+
579743 #[ test]
580744 fn unwrap_some ( ) {
581745 assert_eq ! ( SOME . unwrap( ) , VALUE ) ;
0 commit comments