From ab3fa55a63599e87a101504cb74e6843d2f5e937 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sat, 17 Jan 2026 16:58:43 -0700 Subject: [PATCH] ctutils: add missing `#[must_use]` attributes I ran into a bug `#[must_use]` would've caught and noticed we could use it all over the place. Fortunately, it can be detected and applied automatically with clippy. --- ctutils/src/array.rs | 2 ++ ctutils/src/choice.rs | 6 ++++++ ctutils/src/ct_option.rs | 31 +++++++++++++++++++++++++++++-- ctutils/src/lib.rs | 1 + ctutils/src/slice.rs | 1 + ctutils/src/traits/ct_eq.rs | 2 ++ ctutils/src/traits/ct_find.rs | 1 + ctutils/src/traits/ct_gt.rs | 1 + ctutils/src/traits/ct_lookup.rs | 1 + ctutils/src/traits/ct_lt.rs | 1 + ctutils/src/traits/ct_neg.rs | 1 + ctutils/src/traits/ct_select.rs | 1 + 12 files changed, 47 insertions(+), 2 deletions(-) diff --git a/ctutils/src/array.rs b/ctutils/src/array.rs index 2225cf57..47c52369 100644 --- a/ctutils/src/array.rs +++ b/ctutils/src/array.rs @@ -23,6 +23,7 @@ where /// Generic implementation of constant-time equality testing for arrays which works with any type /// which impls `CtEq`. Useful in the event there isn't a `CtEq` impl for `[T; N]`. #[inline] +#[must_use] pub fn ct_eq(a: &[T; N], b: &[T; N]) -> Choice where T: CtEq, @@ -42,6 +43,7 @@ where /// Unfortunately we can't provide this as a trait impl without specialization, since it would /// overlap with the optimized type-specific impls we provide. #[inline] +#[must_use] pub fn ct_select(a: &[T; N], b: &[T; N], choice: Choice) -> [T; N] where T: CtSelect, diff --git a/ctutils/src/choice.rs b/ctutils/src/choice.rs index 319daa20..c2bfb78a 100644 --- a/ctutils/src/choice.rs +++ b/ctutils/src/choice.rs @@ -51,24 +51,28 @@ impl Choice { /// Apply an `and` conditional to the given [`Choice`]s. #[inline] + #[must_use] pub const fn and(self, rhs: Choice) -> Choice { Self(self.0 & rhs.0) } /// Apply an `or` conditional to the given [`Choice`]s. #[inline] + #[must_use] pub const fn or(self, rhs: Choice) -> Choice { Self(self.0 | rhs.0) } /// Apply an `xor` conditional to the given [`Choice`]s. #[inline] + #[must_use] pub const fn xor(self, rhs: Choice) -> Choice { Self(self.0 ^ rhs.0) } /// Compute the boolean inverse of `self`. #[inline] + #[must_use] pub const fn not(self) -> Choice { // NOTE: assumes self.0 is `0` or `1` as checked in constructor Self(self.0 ^ 1) @@ -80,12 +84,14 @@ impl Choice { /// `const fn` equality operation. #[inline] + #[must_use] pub const fn eq(self, other: Self) -> Self { Self::ne(self, other).not() } /// `const fn` not equal operation. #[inline] + #[must_use] pub const fn ne(self, other: Self) -> Self { Self::xor(self, other) } diff --git a/ctutils/src/ct_option.rs b/ctutils/src/ct_option.rs index b658c983..52a791dc 100644 --- a/ctutils/src/ct_option.rs +++ b/ctutils/src/ct_option.rs @@ -49,18 +49,22 @@ impl CtOption { /// Construct a new [`CtOption`], with a [`Choice`] parameter `is_some` as a stand-in for /// `Some` or `None` enum variants of a typical [`Option`] type. #[inline] + #[must_use] pub const fn new(value: T, is_some: Choice) -> CtOption { Self { value, is_some } } /// Construct a new [`CtOption`] where `self.is_some()` is [`Choice::TRUE`]. #[inline] + #[must_use] pub const fn some(value: T) -> CtOption { Self::new(value, Choice::TRUE) } /// Construct a new [`CtOption`] with the [`Default`] value, and where `self.is_some()` is /// [`Choice::FALSE`]. + #[inline] + #[must_use] pub fn none() -> CtOption where T: Default, @@ -70,6 +74,7 @@ impl CtOption { /// Convert from a `&mut CtOption` to `CtOption<&mut T>`. #[inline] + #[must_use] pub const fn as_mut(&mut self) -> CtOption<&mut T> { CtOption { value: &mut self.value, @@ -79,6 +84,7 @@ impl CtOption { /// Convert from a `&CtOption` to `CtOption<&T>`. #[inline] + #[must_use] pub const fn as_ref(&self) -> CtOption<&T> { CtOption { value: &self.value, @@ -88,6 +94,8 @@ impl CtOption { /// Convert from `CtOption` (or `&CtOption`) to `CtOption<&T::Target>`, for types which /// impl the [`Deref`] trait. + #[inline] + #[must_use] pub fn as_deref(&self) -> CtOption<&T::Target> where T: Deref, @@ -97,6 +105,8 @@ impl CtOption { /// Convert from `CtOption` (or `&mut CtOption`) to `CtOption<&mut T::Target>`, for types /// which impl the [`DerefMut`] trait. + #[inline] + #[must_use] pub fn as_deref_mut(&mut self) -> CtOption<&mut T::Target> where T: DerefMut, @@ -110,6 +120,7 @@ impl CtOption { /// In the event `self.is_some()` is [`Choice::FALSE`], panics with a custom panic message /// provided as the `msg` argument. #[inline] + #[must_use] #[track_caller] pub fn expect(self, msg: &str) -> T { assert!(self.is_some().to_bool(), "{}", msg); @@ -127,6 +138,7 @@ impl CtOption { // TODO(tarcieri): get rid of this when we can make `expect` a `const fn` // (needs `const_precise_live_drops`) #[inline] + #[must_use] #[track_caller] pub const fn expect_copied(self, msg: &str) -> T where @@ -143,6 +155,7 @@ impl CtOption { // TODO(tarcieri): get rid of this when we can make `expect` a `const fn` // (needs `const_precise_live_drops`) #[inline] + #[must_use] #[track_caller] pub const fn expect_ref(&self, msg: &str) -> &T { // TODO(tarcieri): use `self.is_some().to_bool()` when MSRV is 1.86 @@ -234,6 +247,7 @@ impl CtOption { /// Returns `optb` if `self.is_some()` is [`Choice::TRUE`], otherwise returns a [`CtOption`] /// where `self.is_some()` is [`Choice::FALSE`]. #[inline] + #[must_use] pub fn and(self, mut optb: CtOption) -> CtOption { optb.is_some &= self.is_some; optb @@ -248,6 +262,7 @@ impl CtOption { /// (e.g. if the [`CtOption`] was constructed with a supplied placeholder value and /// [`Choice::FALSE`], the placeholder value will be provided). #[inline] + #[must_use] pub fn and_then(self, f: F) -> CtOption where F: FnOnce(T) -> CtOption, @@ -271,6 +286,7 @@ impl CtOption { /// take great care to ensure that `self.is_some()` is checked elsewhere. /// #[inline] + #[must_use] pub const fn as_inner_unchecked(&self) -> &T { &self.value } @@ -281,6 +297,7 @@ impl CtOption { /// It updates it to be [`Choice::FALSE`] in the event the returned choice is also false. /// If it was [`Choice::FALSE`] to begin with, it will unconditionally remain that way. #[inline] + #[must_use] pub fn filter

(mut self, predicate: P) -> Self where P: FnOnce(&T) -> Choice, @@ -291,6 +308,7 @@ impl CtOption { /// Apply an additional [`Choice`] requirement to `is_some`. #[inline] + #[must_use] pub const fn filter_by(mut self, is_some: Choice) -> Self { self.is_some = self.is_some.and(is_some); self @@ -299,6 +317,7 @@ impl CtOption { /// Maps a `CtOption` to a `CtOption` by unconditionally applying a function to the /// contained `value`, but returning a new option value which inherits `self.is_some()`. #[inline] + #[must_use] pub fn map(self, f: F) -> CtOption where F: FnOnce(T) -> U, @@ -322,6 +341,7 @@ impl CtOption { /// `U::default()` using the [`Default`] trait, and returning it in the event `self.is_some()` /// is [`Choice::FALSE`]. #[inline] + #[must_use] pub fn map_or_default(self, f: F) -> U where U: CtSelect + Default, @@ -364,6 +384,7 @@ impl CtOption { /// Returns `self` if `self.is_some()` is [`Choice::TRUE`], otherwise returns `optb`. #[inline] + #[must_use] pub fn or(self, optb: CtOption) -> CtOption where T: CtSelect, @@ -388,6 +409,7 @@ impl CtOption { /// take great care to ensure that `self.is_some()` is checked elsewhere. /// #[inline] + #[must_use] pub const fn to_inner_unchecked(self) -> T where T: Copy, @@ -410,6 +432,8 @@ impl CtOption { /// # Panics /// In the event `self.is_some()` is [`Choice::FALSE`]. #[inline] + #[must_use] + #[track_caller] pub fn unwrap(self) -> T { assert!( self.is_some.to_bool(), @@ -421,6 +445,7 @@ impl CtOption { /// Return the contained value in the event `self.is_some()` is [`Choice::TRUE`], or if not, /// uses a provided default. #[inline] + #[must_use] pub fn unwrap_or(self, default: T) -> T where T: CtSelect, @@ -432,6 +457,7 @@ impl CtOption { /// the contained value if `self.is_some()` is [`Choice::TRUE`], or if it's [`Choice::FALSE`] /// returns the previously computed default. #[inline] + #[must_use] pub fn unwrap_or_default(self) -> T where T: CtSelect + Default, @@ -443,6 +469,7 @@ impl CtOption { /// the event exactly one of them has `self.is_some()` set to [`Choice::TRUE`], or else returns /// a [`CtOption`] with `self.is_some()` set to [`Choice::FALSE`]. #[inline] + #[must_use] pub fn xor(self, optb: CtOption) -> CtOption where T: CtSelect, @@ -694,7 +721,7 @@ mod tests { #[test] #[should_panic] fn expect_none() { - NONE.expect("should panic"); + let _ = NONE.expect("should panic"); } #[test] @@ -834,7 +861,7 @@ mod tests { #[test] #[should_panic] fn unwrap_none() { - NONE.unwrap(); + let _ = NONE.unwrap(); } #[test] diff --git a/ctutils/src/lib.rs b/ctutils/src/lib.rs index 59ed3a0e..3b3bb480 100644 --- a/ctutils/src/lib.rs +++ b/ctutils/src/lib.rs @@ -15,6 +15,7 @@ clippy::mod_module_files, clippy::panic, clippy::panic_in_result_fn, + clippy::return_self_not_must_use, clippy::std_instead_of_alloc, clippy::std_instead_of_core, clippy::undocumented_unsafe_blocks, diff --git a/ctutils/src/slice.rs b/ctutils/src/slice.rs index 2b04b0b4..1bbe78d8 100644 --- a/ctutils/src/slice.rs +++ b/ctutils/src/slice.rs @@ -43,6 +43,7 @@ where target_pointer_width = "64" ))] #[inline] +#[must_use] pub fn ct_eq(a: &[T], b: &[T]) -> Choice where T: CtEq, diff --git a/ctutils/src/traits/ct_eq.rs b/ctutils/src/traits/ct_eq.rs index 5a464935..bcc30416 100644 --- a/ctutils/src/traits/ct_eq.rs +++ b/ctutils/src/traits/ct_eq.rs @@ -22,9 +22,11 @@ where Rhs: ?Sized, { /// Determine if `self` is equal to `other` in constant-time. + #[must_use] fn ct_eq(&self, other: &Rhs) -> Choice; /// Determine if `self` is NOT equal to `other` in constant-time. + #[must_use] fn ct_ne(&self, other: &Rhs) -> Choice { !self.ct_eq(other) } diff --git a/ctutils/src/traits/ct_find.rs b/ctutils/src/traits/ct_find.rs index dd555ceb..79a02647 100644 --- a/ctutils/src/traits/ct_find.rs +++ b/ctutils/src/traits/ct_find.rs @@ -15,6 +15,7 @@ pub trait CtFind { /// /// The first item where `predicate` returns [`Choice::TRUE`] is selected, or the [`CtOption`] /// equivalent of `None` is returned if the `predicate` returns [`Choice::FALSE`] for all items. + #[must_use] fn ct_find

(&self, predicate: P) -> CtOption where P: Fn(&T) -> Choice; diff --git a/ctutils/src/traits/ct_gt.rs b/ctutils/src/traits/ct_gt.rs index 5b6970d0..fcca6033 100644 --- a/ctutils/src/traits/ct_gt.rs +++ b/ctutils/src/traits/ct_gt.rs @@ -7,6 +7,7 @@ use core::{ /// Constant time greater than. pub trait CtGt { /// Compute whether `self > other` in constant time. + #[must_use] fn ct_gt(&self, other: &Self) -> Choice; } diff --git a/ctutils/src/traits/ct_lookup.rs b/ctutils/src/traits/ct_lookup.rs index d4cb5400..5c3db84b 100644 --- a/ctutils/src/traits/ct_lookup.rs +++ b/ctutils/src/traits/ct_lookup.rs @@ -15,6 +15,7 @@ pub trait CtLookup { /// Attempt to retrieve the item at the given `index`, either returning it or the [`CtOption`] /// equivalent of [`None`] if the `index` was out-of-bounds. + #[must_use] fn ct_lookup(&self, index: Idx) -> CtOption; } diff --git a/ctutils/src/traits/ct_lt.rs b/ctutils/src/traits/ct_lt.rs index 7091e8fe..75ea7d4f 100644 --- a/ctutils/src/traits/ct_lt.rs +++ b/ctutils/src/traits/ct_lt.rs @@ -7,6 +7,7 @@ use core::{ /// Constant time less than. pub trait CtLt { /// Compute whether `self < other` in constant time. + #[must_use] fn ct_lt(&self, other: &Self) -> Choice; } diff --git a/ctutils/src/traits/ct_neg.rs b/ctutils/src/traits/ct_neg.rs index 49385b98..51b9ffc0 100644 --- a/ctutils/src/traits/ct_neg.rs +++ b/ctutils/src/traits/ct_neg.rs @@ -8,6 +8,7 @@ use core::num::{ pub trait CtNeg: Sized { /// Conditionally negate `self`, returning `-self` if `choice` is [`Choice::TRUE`], or `self` /// otherwise. + #[must_use] fn ct_neg(&self, choice: Choice) -> Self; /// Conditionally negate `self` in-place, replacing it with `-self` if `choice` is diff --git a/ctutils/src/traits/ct_select.rs b/ctutils/src/traits/ct_select.rs index a39fb7db..0a527091 100644 --- a/ctutils/src/traits/ct_select.rs +++ b/ctutils/src/traits/ct_select.rs @@ -19,6 +19,7 @@ pub trait CtSelect: Sized { /// # Returns /// - `self` if `choice` is [`Choice::FALSE`]. /// - `other` if `choice` is [`Choice::TRUE`]. + #[must_use] fn ct_select(&self, other: &Self, choice: Choice) -> Self; /// Conditionally swap `self` and `other` if `choice` is [`Choice::TRUE`].