pub struct HashableScalar<ScalarType: FpScalar> { /* private fields */ }Expand description
Wrapper type that makes floating-point scalars hashable for use in hash-based collections.
§Purpose
Real or complex floating-point types (f32, f64, Complex<f32>, Complex<f64> and validated wrappers like
RealNative64StrictFinite,
ComplexNative64StrictFinite, RealRugStrictFinite<PRECISION>,
ComplexRugStrictFinite<PRECISION>, etc.) are not hashable by default
in Rust because of the presence of NaN values, which violate the requirements for hashing. Specifically, IEEE 754 defines that NaN != NaN, which means that two identical NaN values would not be considered equal, leading to inconsistent hashing behavior.
do not implement Hash by default because IEEE 754 defines NaN != NaN, violating the
requirement that a == b implies hash(a) == hash(b). This type enforces that only
finite floating-point values can be hashed, making them safe for use in:
HashMapandHashSetDashMapfor concurrent caching- Any algorithm requiring consistent hashing of numerical values
§Invariants
- Only finite values allowed:
HashableScalar::try_new()rejects values that are not finite (notNaNor±∞). - Hash consistency: Values are hashed via their raw bit representation
(
RawScalarTrait::compute_hash_raw_scalar), ensuring bitwise-identical values produce identical hashes. - Equality semantics: Implements
Eqby delegating to the underlying scalar’sPartialEq, which is safe because we excludeNaN.
§Type Parameters
ScalarType: Any real or complex floating-point type implementingFpScalar(e.g.,f64,Complex<f64>,RealNative64StrictFinite,ComplexNative64StrictFinite,RealRugStrictFinite<PRECISION>,ComplexRugStrictFinite<PRECISION>, etc.).
§Examples
§Basic Usage with f64 and Complex<f64>
use num_valid::scalars::HashableScalar;
use num::Complex;
use std::collections::HashSet;
let x = HashableScalar::new(3.14);
let y = HashableScalar::new(2.71);
let z = HashableScalar::new(3.14);
let mut set_real = HashSet::new();
set_real.insert(x);
set_real.insert(y);
set_real.insert(z); // Duplicate, won't be added
assert_eq!(set_real.len(), 2);
let a = HashableScalar::new(Complex::new(3.14, 2.0));
let b = HashableScalar::new(Complex::new(2.71, 1.0));
let c = HashableScalar::new(Complex::new(3.14, 2.0));
let mut set_complex = HashSet::new();
set_complex.insert(a);
set_complex.insert(b);
set_complex.insert(c); // Duplicate, won't be added
assert_eq!(set_complex.len(), 2);§Use in Caching Structures
use num_valid::scalars::HashableScalar;
use std::collections::HashMap;
// Cache computed values indexed by floating-point parameters
let mut cache: HashMap<HashableScalar<f64>, String> = HashMap::new();
let key = HashableScalar::new(1.5);
cache.insert(key, "result_for_1.5".to_string());
let retrieved = cache.get(&HashableScalar::new(1.5));
assert_eq!(retrieved, Some(&"result_for_1.5".to_string()));§With Validated Scalar Types
The wrapper works with any type implementing FpScalar, including validated types
from the num_valid crate:
use num_valid::scalars::HashableScalar;
use std::collections::HashSet;
// Works with any FpScalar type
let mut set = HashSet::new();
set.insert(HashableScalar::new(1.0_f64));
set.insert(HashableScalar::new(2.0_f64));
set.insert(HashableScalar::new(1.0_f64)); // Duplicate
assert_eq!(set.len(), 2);§Invalid Values
Fallible construction rejects non-finite values:
use num_valid::scalars::HashableScalar;
let invalid = HashableScalar::try_new(f64::NAN);
assert!(invalid.is_err());§Implementation Notes
- Serialization: Supports
serdeserialization/deserialization with bounds to ensure the wrapped type can be serialized. - Extraction: Implements
IntoInnerfor consuming the wrapper and extracting the underlying scalar via.into_inner(). - Zero-cost abstraction: The wrapper is
#[repr(transparent)]equivalent (single field), so there is no runtime overhead.
§See Also
FpScalarfor the trait bounds on wrapped typesRawScalarTrait::compute_hash_raw_scalarfor the hashing mechanism- Caching examples in
numerics-core/src/math/chebyshev/cache.rs
Implementations§
Source§impl<ScalarType: FpScalar> HashableScalar<ScalarType>
impl<ScalarType: FpScalar> HashableScalar<ScalarType>
Sourcepub fn new(value: ScalarType) -> Self
pub fn new(value: ScalarType) -> Self
Creates a new hashable scalar from a finite value.
§Panics
Panics if the value is not finite (NaN or infinite). Use Self::try_new
when the input is not already trusted.
Sourcepub fn try_new(value: ScalarType) -> Result<Self, ErrorsHashableScalar>
pub fn try_new(value: ScalarType) -> Result<Self, ErrorsHashableScalar>
Attempts to create a hashable scalar from a finite value.
Returns ErrorsHashableScalar::NonFiniteValue when value is NaN or infinite.
Trait Implementations§
Source§impl<ScalarType: FpScalar> AsRef<ScalarType> for HashableScalar<ScalarType>
impl<ScalarType: FpScalar> AsRef<ScalarType> for HashableScalar<ScalarType>
Source§fn as_ref(&self) -> &ScalarType
fn as_ref(&self) -> &ScalarType
Source§impl<ScalarType: Clone + FpScalar> Clone for HashableScalar<ScalarType>
impl<ScalarType: Clone + FpScalar> Clone for HashableScalar<ScalarType>
Source§fn clone(&self) -> HashableScalar<ScalarType>
fn clone(&self) -> HashableScalar<ScalarType>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl<'de, ScalarType> Deserialize<'de> for HashableScalar<ScalarType>where
ScalarType: for<'a> Deserialize<'a> + FpScalar,
impl<'de, ScalarType> Deserialize<'de> for HashableScalar<ScalarType>where
ScalarType: for<'a> Deserialize<'a> + FpScalar,
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl<ScalarType: FpScalar> Eq for HashableScalar<ScalarType>
Source§impl<ScalarType: FpScalar> Hash for HashableScalar<ScalarType>
impl<ScalarType: FpScalar> Hash for HashableScalar<ScalarType>
Source§impl<ScalarType: FpScalar> IntoInner for HashableScalar<ScalarType>
impl<ScalarType: FpScalar> IntoInner for HashableScalar<ScalarType>
Source§impl<ScalarType: PartialOrd + FpScalar> PartialOrd for HashableScalar<ScalarType>
impl<ScalarType: PartialOrd + FpScalar> PartialOrd for HashableScalar<ScalarType>
Source§impl<ScalarType> Serialize for HashableScalar<ScalarType>
impl<ScalarType> Serialize for HashableScalar<ScalarType>
impl<ScalarType: PartialEq + FpScalar> StructuralPartialEq for HashableScalar<ScalarType>
Auto Trait Implementations§
impl<ScalarType> Freeze for HashableScalar<ScalarType>where
ScalarType: Freeze,
impl<ScalarType> RefUnwindSafe for HashableScalar<ScalarType>where
ScalarType: RefUnwindSafe,
impl<ScalarType> Send for HashableScalar<ScalarType>
impl<ScalarType> Sync for HashableScalar<ScalarType>
impl<ScalarType> Unpin for HashableScalar<ScalarType>where
ScalarType: Unpin,
impl<ScalarType> UnsafeUnpin for HashableScalar<ScalarType>where
ScalarType: UnsafeUnpin,
impl<ScalarType> UnwindSafe for HashableScalar<ScalarType>where
ScalarType: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CheckedAs for T
impl<T> CheckedAs for T
Source§fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
Source§impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
Source§fn checked_cast_from(src: Src) -> Option<Dst>
fn checked_cast_from(src: Src) -> Option<Dst>
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more