【数值特性库】入口文件

数值特性库入口文件为lib.rs。该文件定义一系列数字特性的trait(特征),这些特性可以被不同的数字类型实现,从而提供一套通用的数值操作方法。下面是对代码中关键部分的解释:

一、基础设置

  • #![doc(html_root_url = "https://docs.rs/num-traits/0.2")]:指定了文档的根URL,用于在线文档生成。
  • #![deny(unconditional_recursion)]:禁止无条件递归,这是一种编译时检查,防止无限递归。
  • #![no_std]:表明这个crate不依赖Rust标准库,使其可以在没有标准库的环境(如裸机或嵌入式系统)中使用。

二、条件编译

  • #[cfg(feature = "std")]:当启用std特性时,编译这部分代码。这通常用于在有无标准库支持时提供不同的实现。

三、引入依赖

  • 引入了Rust核心库中的一些基本功能,如格式化(fmt)、包装类型(Wrapping)、基本的算术操作(Add, Div, Mul, Rem, Sub等)及其赋值操作(AddAssign, DivAssign, MulAssign, RemAssign, SubAssign)。

四、公开的特性

  • 通过pub use语句,公开了库中定义的一系列特性(traits)和常量,使得外部可以直接通过这些路径访问它们。例如,Bounded用于表示有边界的数字类型,Float和FloatConst提供了浮点数的操作和常量,NumCast用于类型转换等。

五、核心trait定义:

  • Num:定义了数值类型的基础特性,包括比较、基本数值操作、字符串转换等。
  • NumOps:为实现了基本算术运算符(+, -, *, /, %)的类型自动实现。
  • NumRef和RefNum:提供了对引用类型数值操作的支持。
  • NumAssignOps:为实现了赋值运算符(如+=, -=)的类型自动实现。

六、宏和模块:

  • 通过#[macro_use]引入了宏定义(在macros模块中),这些宏可能用于简化代码或提供额外的功能。
  • 定义了多个模块(如bounds, cast, float, identities, int, ops, pow, real, sign),每个模块都负责特定的数值操作或特性。

七、总结及源码

整体而言,这段代码定义了一个丰富的数字特性库,为Rust中的数值类型提供了一套通用的接口和操作方法。通过实现这些trait,不同的数值类型可以享受到这些通用操作带来的便利,同时也为开发者提供了一种灵活的方式来处理不同类型的数值。源码如下:

rust 复制代码
//!为泛型准备的数字特征库 

#![doc(html_root_url = "https://docs.rs/num-traits/0.2")]
#![deny(unconditional_recursion)]
#![no_std]

// 需要显式地将crate引入固有的float方法。Need to explicitly bring the crate in for inherent float methods
#[cfg(feature = "std")]
extern crate std;

use core::fmt;
use core::num::Wrapping;
use core::ops::{Add, Div, Mul, Rem, Sub};
use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};

pub use crate::bounds::Bounded; // 1 边界特性
#[cfg(any(feature = "std", feature = "libm"))]
pub use crate::float::Float; // 2
pub use crate::float::FloatConst; // 3

pub use crate::cast::{cast, AsPrimitive, FromPrimitive, NumCast, ToPrimitive}; // 4
pub use crate::identities::{one, zero, ConstOne, ConstZero, One, Zero}; // 5
pub use crate::int::PrimInt; // 6
pub use crate::ops::bytes::{FromBytes, ToBytes}; // 7
pub use crate::ops::checked::{
    CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub,
};               // 8
pub use crate::ops::euclid::{CheckedEuclid, Euclid}; // 9
pub use crate::ops::inv::Inv; // 10
pub use crate::ops::mul_add::{MulAdd, MulAddAssign}; // 11
pub use crate::ops::saturating::{Saturating, SaturatingAdd, SaturatingMul, SaturatingSub}; // 12
pub use crate::ops::wrapping::{
    WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub,
};                           // 13
pub use crate::pow::{checked_pow, pow, Pow};  // 14
pub use crate::sign::{abs, abs_sub, signum, Signed, Unsigned};  // 15

#[macro_use]
mod macros;

pub mod bounds;
pub mod cast;
pub mod float;
pub mod identities;
pub mod int;
pub mod ops;
pub mod pow;
pub mod real;
pub mod sign;

/// The base trait for numeric types, covering `0` and `1` values,
/// comparisons, basic numeric operations, and string conversion.
pub trait Num: PartialEq + Zero + One + NumOps {
    type FromStrRadixErr;

    /// Convert from a string and radix (typically `2..=36`).
    ///
    /// # Examples
    ///
    /// ```rust
    /// use num_traits::Num;
    ///
    /// let result = <i32 as Num>::from_str_radix("27", 10);
    /// assert_eq!(result, Ok(27));
    ///
    /// let result = <i32 as Num>::from_str_radix("foo", 10);
    /// assert!(result.is_err());
    /// ```
    ///
    /// # Supported radices
    ///
    /// The exact range of supported radices is at the discretion of each type implementation. For
    /// primitive integers, this is implemented by the inherent `from_str_radix` methods in the
    /// standard library, which **panic** if the radix is not in the range from 2 to 36. The
    /// implementation in this crate for primitive floats is similar.
    ///
    /// For third-party types, it is suggested that implementations should follow suit and at least
    /// accept `2..=36` without panicking, but an `Err` may be returned for any unsupported radix.
    /// It's possible that a type might not even support the common radix 10, nor any, if string
    /// parsing doesn't make sense for that type.
    fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr>;
}

/// Generic trait for types implementing basic numeric operations
///
/// This is automatically implemented for types which implement the operators.
pub trait NumOps<Rhs = Self, Output = Self>:
    Add<Rhs, Output = Output>
    + Sub<Rhs, Output = Output>
    + Mul<Rhs, Output = Output>
    + Div<Rhs, Output = Output>
    + Rem<Rhs, Output = Output>
{
}

impl<T, Rhs, Output> NumOps<Rhs, Output> for T where
    T: Add<Rhs, Output = Output>
        + Sub<Rhs, Output = Output>
        + Mul<Rhs, Output = Output>
        + Div<Rhs, Output = Output>
        + Rem<Rhs, Output = Output>
{
}

/// The trait for `Num` types which also implement numeric operations taking
/// the second operand by reference.
///
/// This is automatically implemented for types which implement the operators.
pub trait NumRef: Num + for<'r> NumOps<&'r Self> {}
impl<T> NumRef for T where T: Num + for<'r> NumOps<&'r T> {}

/// The trait for `Num` references which implement numeric operations, taking the
/// second operand either by value or by reference.
///
/// This is automatically implemented for all types which implement the operators. It covers
/// every type implementing the operations though, regardless of it being a reference or
/// related to `Num`.
pub trait RefNum<Base>: NumOps<Base, Base> + for<'r> NumOps<&'r Base, Base> {}
impl<T, Base> RefNum<Base> for T where T: NumOps<Base, Base> + for<'r> NumOps<&'r Base, Base> {}

/// Generic trait for types implementing numeric assignment operators (like `+=`).
///
/// This is automatically implemented for types which implement the operators.
pub trait NumAssignOps<Rhs = Self>:
    AddAssign<Rhs> + SubAssign<Rhs> + MulAssign<Rhs> + DivAssign<Rhs> + RemAssign<Rhs>
{
}

impl<T, Rhs> NumAssignOps<Rhs> for T where
    T: AddAssign<Rhs> + SubAssign<Rhs> + MulAssign<Rhs> + DivAssign<Rhs> + RemAssign<Rhs>
{
}

/// The trait for `Num` types which also implement assignment operators.
///
/// This is automatically implemented for types which implement the operators.
pub trait NumAssign: Num + NumAssignOps {}
impl<T> NumAssign for T where T: Num + NumAssignOps {}

/// The trait for `NumAssign` types which also implement assignment operations
/// taking the second operand by reference.
///
/// This is automatically implemented for types which implement the operators.
pub trait NumAssignRef: NumAssign + for<'r> NumAssignOps<&'r Self> {}
impl<T> NumAssignRef for T where T: NumAssign + for<'r> NumAssignOps<&'r T> {}

macro_rules! int_trait_impl {
    ($name:ident for $($t:ty)*) => ($(
        impl $name for $t {
            type FromStrRadixErr = ::core::num::ParseIntError;
            #[inline]
            fn from_str_radix(s: &str, radix: u32)
                              -> Result<Self, ::core::num::ParseIntError>
            {
                <$t>::from_str_radix(s, radix)
            }
        }
    )*)
}
int_trait_impl!(Num for usize u8 u16 u32 u64 u128);
int_trait_impl!(Num for isize i8 i16 i32 i64 i128);

impl<T: Num> Num for Wrapping<T>
where
    Wrapping<T>: NumOps,
{
    type FromStrRadixErr = T::FromStrRadixErr;
    fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
        T::from_str_radix(str, radix).map(Wrapping)
    }
}

#[derive(Debug)]
pub enum FloatErrorKind {
    Empty,
    Invalid,
}
// FIXME: core::num::ParseFloatError is stable in 1.0, but opaque to us,
// so there's not really any way for us to reuse it.
#[derive(Debug)]
pub struct ParseFloatError {
    pub kind: FloatErrorKind,
}

impl fmt::Display for ParseFloatError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let description = match self.kind {
            FloatErrorKind::Empty => "cannot parse float from empty string",
            FloatErrorKind::Invalid => "invalid float literal",
        };

        description.fmt(f)
    }
}

fn str_to_ascii_lower_eq_str(a: &str, b: &str) -> bool {
    a.len() == b.len()
        && a.bytes().zip(b.bytes()).all(|(a, b)| {
            let a_to_ascii_lower = a | (((b'A' <= a && a <= b'Z') as u8) << 5);
            a_to_ascii_lower == b
        })
}

// FIXME: The standard library from_str_radix on floats was deprecated, so we're stuck
// with this implementation ourselves until we want to make a breaking change.
// (would have to drop it from `Num` though)
macro_rules! float_trait_impl {
    ($name:ident for $($t:ident)*) => ($(
        impl $name for $t {
            type FromStrRadixErr = ParseFloatError;

            fn from_str_radix(src: &str, radix: u32)
                              -> Result<Self, Self::FromStrRadixErr>
            {
                use self::FloatErrorKind::*;
                use self::ParseFloatError as PFE;

                // Special case radix 10 to use more accurate standard library implementation
                if radix == 10 {
                    return src.parse().map_err(|_| PFE {
                        kind: if src.is_empty() { Empty } else { Invalid },
                    });
                }

                // Special values
                if str_to_ascii_lower_eq_str(src, "inf")
                    || str_to_ascii_lower_eq_str(src, "infinity")
                {
                    return Ok(core::$t::INFINITY);
                } else if str_to_ascii_lower_eq_str(src, "-inf")
                    || str_to_ascii_lower_eq_str(src, "-infinity")
                {
                    return Ok(core::$t::NEG_INFINITY);
                } else if str_to_ascii_lower_eq_str(src, "nan") {
                    return Ok(core::$t::NAN);
                } else if str_to_ascii_lower_eq_str(src, "-nan") {
                    return Ok(-core::$t::NAN);
                }

                fn slice_shift_char(src: &str) -> Option<(char, &str)> {
                    let mut chars = src.chars();
                    Some((chars.next()?, chars.as_str()))
                }

                let (is_positive, src) =  match slice_shift_char(src) {
                    None             => return Err(PFE { kind: Empty }),
                    Some(('-', ""))  => return Err(PFE { kind: Empty }),
                    Some(('-', src)) => (false, src),
                    Some((_, _))     => (true,  src),
                };

                // The significand to accumulate
                let mut sig = if is_positive { 0.0 } else { -0.0 };
                // Necessary to detect overflow
                let mut prev_sig = sig;
                let mut cs = src.chars().enumerate();
                // Exponent prefix and exponent index offset
                let mut exp_info = None::<(char, usize)>;

                // Parse the integer part of the significand
                for (i, c) in cs.by_ref() {
                    match c.to_digit(radix) {
                        Some(digit) => {
                            // shift significand one digit left
                            sig *= radix as $t;

                            // add/subtract current digit depending on sign
                            if is_positive {
                                sig += (digit as isize) as $t;
                            } else {
                                sig -= (digit as isize) as $t;
                            }

                            // Detect overflow by comparing to last value, except
                            // if we've not seen any non-zero digits.
                            if prev_sig != 0.0 {
                                if is_positive && sig <= prev_sig
                                    { return Ok(core::$t::INFINITY); }
                                if !is_positive && sig >= prev_sig
                                    { return Ok(core::$t::NEG_INFINITY); }

                                // Detect overflow by reversing the shift-and-add process
                                if is_positive && (prev_sig != (sig - digit as $t) / radix as $t)
                                    { return Ok(core::$t::INFINITY); }
                                if !is_positive && (prev_sig != (sig + digit as $t) / radix as $t)
                                    { return Ok(core::$t::NEG_INFINITY); }
                            }
                            prev_sig = sig;
                        },
                        None => match c {
                            'e' | 'E' | 'p' | 'P' => {
                                exp_info = Some((c, i + 1));
                                break;  // start of exponent
                            },
                            '.' => {
                                break;  // start of fractional part
                            },
                            _ => {
                                return Err(PFE { kind: Invalid });
                            },
                        },
                    }
                }

                // If we are not yet at the exponent parse the fractional
                // part of the significand
                if exp_info.is_none() {
                    let mut power = 1.0;
                    for (i, c) in cs.by_ref() {
                        match c.to_digit(radix) {
                            Some(digit) => {
                                // Decrease power one order of magnitude
                                power /= radix as $t;
                                // add/subtract current digit depending on sign
                                sig = if is_positive {
                                    sig + (digit as $t) * power
                                } else {
                                    sig - (digit as $t) * power
                                };
                                // Detect overflow by comparing to last value
                                if is_positive && sig < prev_sig
                                    { return Ok(core::$t::INFINITY); }
                                if !is_positive && sig > prev_sig
                                    { return Ok(core::$t::NEG_INFINITY); }
                                prev_sig = sig;
                            },
                            None => match c {
                                'e' | 'E' | 'p' | 'P' => {
                                    exp_info = Some((c, i + 1));
                                    break; // start of exponent
                                },
                                _ => {
                                    return Err(PFE { kind: Invalid });
                                },
                            },
                        }
                    }
                }

                // Parse and calculate the exponent
                let exp = match exp_info {
                    Some((c, offset)) => {
                        let base = match c {
                            'E' | 'e' if radix == 10 => 10.0,
                            'P' | 'p' if radix == 16 => 2.0,
                            _ => return Err(PFE { kind: Invalid }),
                        };

                        // Parse the exponent as decimal integer
                        let src = &src[offset..];
                        let (is_positive, exp) = match slice_shift_char(src) {
                            Some(('-', src)) => (false, src.parse::<usize>()),
                            Some(('+', src)) => (true,  src.parse::<usize>()),
                            Some((_, _))     => (true,  src.parse::<usize>()),
                            None             => return Err(PFE { kind: Invalid }),
                        };

                        #[cfg(feature = "std")]
                        fn pow(base: $t, exp: usize) -> $t {
                            Float::powi(base, exp as i32)
                        }
                        // otherwise uses the generic `pow` from the root

                        match (is_positive, exp) {
                            (true,  Ok(exp)) => pow(base, exp),
                            (false, Ok(exp)) => 1.0 / pow(base, exp),
                            (_, Err(_))      => return Err(PFE { kind: Invalid }),
                        }
                    },
                    None => 1.0, // no exponent
                };

                Ok(sig * exp)
            }
        }
    )*)
}
float_trait_impl!(Num for f32 f64);

/// A value bounded by a minimum and a maximum
///
///  If input is less than min then this returns min.
///  If input is greater than max then this returns max.
///  Otherwise this returns input.
///
/// **Panics** in debug mode if `!(min <= max)`.
#[inline]
pub fn clamp<T: PartialOrd>(input: T, min: T, max: T) -> T {
    debug_assert!(min <= max, "min must be less than or equal to max");
    if input < min {
        min
    } else if input > max {
        max
    } else {
        input
    }
}

/// A value bounded by a minimum value
///
///  If input is less than min then this returns min.
///  Otherwise this returns input.
///  `clamp_min(std::f32::NAN, 1.0)` preserves `NAN` different from `f32::min(std::f32::NAN, 1.0)`.
///
/// **Panics** in debug mode if `!(min == min)`. (This occurs if `min` is `NAN`.)
#[inline]
#[allow(clippy::eq_op)]
pub fn clamp_min<T: PartialOrd>(input: T, min: T) -> T {
    debug_assert!(min == min, "min must not be NAN");
    if input < min {
        min
    } else {
        input
    }
}

/// A value bounded by a maximum value
///
///  If input is greater than max then this returns max.
///  Otherwise this returns input.
///  `clamp_max(std::f32::NAN, 1.0)` preserves `NAN` different from `f32::max(std::f32::NAN, 1.0)`.
///
/// **Panics** in debug mode if `!(max == max)`. (This occurs if `max` is `NAN`.)
#[inline]
#[allow(clippy::eq_op)]
pub fn clamp_max<T: PartialOrd>(input: T, max: T) -> T {
    debug_assert!(max == max, "max must not be NAN");
    if input > max {
        max
    } else {
        input
    }
}

#[test]
fn clamp_test() {
    // Int test
    assert_eq!(1, clamp(1, -1, 2));
    assert_eq!(-1, clamp(-2, -1, 2));
    assert_eq!(2, clamp(3, -1, 2));
    assert_eq!(1, clamp_min(1, -1));
    assert_eq!(-1, clamp_min(-2, -1));
    assert_eq!(-1, clamp_max(1, -1));
    assert_eq!(-2, clamp_max(-2, -1));

    // Float test
    assert_eq!(1.0, clamp(1.0, -1.0, 2.0));
    assert_eq!(-1.0, clamp(-2.0, -1.0, 2.0));
    assert_eq!(2.0, clamp(3.0, -1.0, 2.0));
    assert_eq!(1.0, clamp_min(1.0, -1.0));
    assert_eq!(-1.0, clamp_min(-2.0, -1.0));
    assert_eq!(-1.0, clamp_max(1.0, -1.0));
    assert_eq!(-2.0, clamp_max(-2.0, -1.0));
    assert!(clamp(::core::f32::NAN, -1.0, 1.0).is_nan());
    assert!(clamp_min(::core::f32::NAN, 1.0).is_nan());
    assert!(clamp_max(::core::f32::NAN, 1.0).is_nan());
}

#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_nan_min() {
    clamp(0., ::core::f32::NAN, 1.);
}

#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_nan_max() {
    clamp(0., -1., ::core::f32::NAN);
}

#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_nan_min_max() {
    clamp(0., ::core::f32::NAN, ::core::f32::NAN);
}

#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_min_nan_min() {
    clamp_min(0., ::core::f32::NAN);
}

#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_max_nan_max() {
    clamp_max(0., ::core::f32::NAN);
}

#[test]
fn from_str_radix_unwrap() {
    // The Result error must impl Debug to allow unwrap()

    let i: i32 = Num::from_str_radix("0", 10).unwrap();
    assert_eq!(i, 0);

    let f: f32 = Num::from_str_radix("0.0", 10).unwrap();
    assert_eq!(f, 0.0);
}

#[test]
fn from_str_radix_multi_byte_fail() {
    // Ensure parsing doesn't panic, even on invalid sign characters
    assert!(f32::from_str_radix("™0.2", 10).is_err());

    // Even when parsing the exponent sign
    assert!(f32::from_str_radix("0.2E™1", 10).is_err());
}

#[test]
fn from_str_radix_ignore_case() {
    assert_eq!(
        f32::from_str_radix("InF", 16).unwrap(),
        ::core::f32::INFINITY
    );
    assert_eq!(
        f32::from_str_radix("InfinitY", 16).unwrap(),
        ::core::f32::INFINITY
    );
    assert_eq!(
        f32::from_str_radix("-InF", 8).unwrap(),
        ::core::f32::NEG_INFINITY
    );
    assert_eq!(
        f32::from_str_radix("-InfinitY", 8).unwrap(),
        ::core::f32::NEG_INFINITY
    );
    assert!(f32::from_str_radix("nAn", 4).unwrap().is_nan());
    assert!(f32::from_str_radix("-nAn", 4).unwrap().is_nan());
}

#[test]
fn wrapping_is_num() {
    fn require_num<T: Num>(_: &T) {}
    require_num(&Wrapping(42_u32));
    require_num(&Wrapping(-42));
}

#[test]
fn wrapping_from_str_radix() {
    macro_rules! test_wrapping_from_str_radix {
        ($($t:ty)+) => {
            $(
                for &(s, r) in &[("42", 10), ("42", 2), ("-13.0", 10), ("foo", 10)] {
                    let w = Wrapping::<$t>::from_str_radix(s, r).map(|w| w.0);
                    assert_eq!(w, <$t as Num>::from_str_radix(s, r));
                }
            )+
        };
    }

    test_wrapping_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}

#[test]
fn check_num_ops() {
    fn compute<T: Num + Copy>(x: T, y: T) -> T {
        x * y / y % y + y - y
    }
    assert_eq!(compute(1, 2), 1)
}

#[test]
fn check_numref_ops() {
    fn compute<T: NumRef>(x: T, y: &T) -> T {
        x * y / y % y + y - y
    }
    assert_eq!(compute(1, &2), 1)
}

#[test]
fn check_refnum_ops() {
    fn compute<T: Copy>(x: &T, y: T) -> T
    where
        for<'a> &'a T: RefNum<T>,
    {
        &(&(&(&(x * y) / y) % y) + y) - y
    }
    assert_eq!(compute(&1, 2), 1)
}

#[test]
fn check_refref_ops() {
    fn compute<T>(x: &T, y: &T) -> T
    where
        for<'a> &'a T: RefNum<T>,
    {
        &(&(&(&(x * y) / y) % y) + y) - y
    }
    assert_eq!(compute(&1, &2), 1)
}

#[test]
fn check_numassign_ops() {
    fn compute<T: NumAssign + Copy>(mut x: T, y: T) -> T {
        x *= y;
        x /= y;
        x %= y;
        x += y;
        x -= y;
        x
    }
    assert_eq!(compute(1, 2), 1)
}

#[test]
fn check_numassignref_ops() {
    fn compute<T: NumAssignRef + Copy>(mut x: T, y: &T) -> T {
        x *= y;
        x /= y;
        x %= y;
        x += y;
        x -= y;
        x
    }
    assert_eq!(compute(1, &2), 1)
}
相关推荐
DongLi012 天前
rustlings 学习笔记 -- exercises/05_vecs
rust
番茄灭世神3 天前
Rust学习笔记第2篇
rust·编程语言
shimly1234563 天前
(done) 速通 rustlings(20) 错误处理1 --- 不涉及Traits
rust
shimly1234563 天前
(done) 速通 rustlings(19) Option
rust
@atweiwei3 天前
rust所有权机制详解
开发语言·数据结构·后端·rust·内存·所有权
shimly1234563 天前
(done) 速通 rustlings(24) 错误处理2 --- 涉及Traits
rust
shimly1234563 天前
(done) 速通 rustlings(23) 特性 Traits
rust
shimly1234563 天前
(done) 速通 rustlings(17) 哈希表
rust
shimly1234563 天前
(done) 速通 rustlings(15) 字符串
rust
shimly1234563 天前
(done) 速通 rustlings(22) 泛型
rust