#转换 #近似 #转换 # #

无std conv2

此包提供了一组比'as'或'From'/'Into'提供的更具体语义的转换特质。

1个不稳定发布

0.4.0-alpha.12023年12月29日

#1104Rust模式

MIT 许可证

70KB
1K SLoC

conv2 是由 Daniel Keep 编写的 conv 的分支。它正在更新以适应现代的 Rust 习惯。

此包提供了一组比 asFrom/Into 提供的更具体语义的转换特质。

这里提供的特质的目标是更具体地说明泛型代码可以依赖的内容,并提供对标准 From/Into 特质的合理自描述替代方案。例如,虽然 T: From<U> 可能满足,但它对实现的 类型 没有任何限制。因此,此包中的特质试图非常具体地说明允许的转换类型。这使得它们更适用于特定情况,但在适用时更有用。

此外,From/Into 要求所有转换都成功或崩溃。此包中定义的所有转换特质都有一个关联的错误类型,允许代码根据需要对失败的转换做出反应。

兼容性

conv2 与 Rust 1.56 及更高版本兼容。

概述

以下特质用于定义各种转换语义

在定义转换时,尽可能实现*From特质变体。在使用转换时,尽可能依赖*Into特质变体。这是因为*Into特质会自动使用*From实现,但反过来则不行。实现*From和使用*Into可以确保转换在尽可能多的上下文中工作。

这些扩展方法提供了一些常见情况的帮助。

提供的实现

该软件包提供了几个泛型实现

  • *From<A> for A(所有类型都可以相互转换)。
  • *Into<Dst> for Src where Dst: *From<Src>*From 实现隐含相应的 *Into 实现)。

提供了内置数值类型(整数和浮点数)的转换。一般来说,除了 float → integer(这种转换通常不太可能 完全 成功)和 f64f32(同样原因),所有数值对都存在 ValueFrom 转换。所有对之间都存在使用 DefaultApprox 方案的 ApproxFrom 转换。整数之间存在使用 Wrapping 方案的 ApproxFrom 转换。

错误

errors 模块中定义了多种错误类型。通常,转换使用最 精确 地定义可能发生的失败类型的错误类型。例如

  • ValueFrom<u8> for u16 不可能失败,因此它使用 NoError
  • ValueFrom<i8> for u16 只能因负溢出而失败,因此它使用 NegOverflow 类型。
  • ValueFrom<i32> for u16 可以在两个方向上溢出,因此它使用 RangeError
  • 最后,ApproxFrom<f32> for u16 可以溢出(正或负),或尝试转换 NaN;FloatError 覆盖这三种情况。

由于存在许多错误类型,因此提供了GeneralError枚举。对于此crate定义的每个错误类型E<T>(即使是NoError!),都存在一个From<E, T> for GeneralError<T>,这使得错误可以通过?运算符自动转换。实际上,所有错误都可以“展开”到所有更一般的格式(例如,NoErrorNegOverflowPosOverflowRangeErrorFloatError)。

除了NoError之外,各种错误类型都包装了您尝试转换的输入值。这样,非Copy类型在转换之前无需预先克隆,以防转换失败。缺点是这意味着存在许多、许多不兼容的错误类型。

为了缓解这个问题,还存在GeneralErrorKind,它只是没有有效负载的GeneralError<T>,所有错误都可以直接转换成它。

最初没有直接使用GeneralErrorKind的原因是为了静态地减少您需要处理的潜在错误情况的数量。它还允许定义没有运行时失败可能的Unwrap*扩展特质(例如,您不能使用unwrap_or_saturate与一个FloatError,因为如果错误是NotANumber,您该怎么办;饱和到最大值或最小值?或者恐慌?)。

示例


// This *cannot* fail, so we can use `unwrap_ok` to discard the `Result`.
assert_eq!(u8::value_from(0u8).unwrap_ok(), 0u8);

// This *can* fail. Specifically, it can overflow toward negative infinity.
assert_eq!(u8::value_from(0i8),     Ok(0u8));
assert_eq!(u8::value_from(-1i8),    Err(NegOverflow(-1)));

// This can overflow in *either* direction; hence the change to `RangeError`.
assert_eq!(u8::value_from(-1i16),   Err(RangeError::NegOverflow(-1)));
assert_eq!(u8::value_from(0i16),    Ok(0u8));
assert_eq!(u8::value_from(256i16),  Err(RangeError::PosOverflow(256)));

// We can use the extension traits to simplify this a little.
assert_eq!(u8::value_from(-1i16).unwrap_or_saturate(),  0u8);
assert_eq!(u8::value_from(0i16).unwrap_or_saturate(),   0u8);
assert_eq!(u8::value_from(256i16).unwrap_or_saturate(), 255u8);

// Obviously, all integers can be "approximated" using the default scheme (it
// doesn't *do* anything), but they can *also* be approximated with the
// `Wrapping` scheme.
assert_eq!(
    <u8 as ApproxFrom<_, DefaultApprox>>::approx_from(400u16),
    Err(PosOverflow(400)));
assert_eq!(
    <u8 as ApproxFrom<_, Wrapping>>::approx_from(400u16),
    Ok(144u8));

// This is rather inconvenient; as such, there are a number of convenience
// extension methods available via `ConvUtil` and `ConvAsUtil`.
assert_eq!(400u16.approx(),                       Err::<u8, _>(PosOverflow(400)));
assert_eq!(400u16.approx_by::<Wrapping>(),        Ok::<u8, _>(144u8));
assert_eq!(400u16.approx_as::<u8>(),              Err(PosOverflow(400)));
assert_eq!(400u16.approx_as_by::<u8, Wrapping>(), Ok(144));

// Integer -> float conversions *can* fail due to limited precision.
// Once the continuous range of exactly representable integers is exceeded, the
// provided implementations fail with overflow errors.
assert_eq!(f32::value_from(16_777_216i32), Ok(16_777_216.0f32));
assert_eq!(f32::value_from(16_777_217i32), Err(RangeError::PosOverflow(16_777_217)));

// Float -> integer conversions have to be done using approximations. Although
// exact conversions are *possible*, "advertising" this with an implementation
// is misleading.
//
// Note that `DefaultApprox` for float -> integer uses whatever rounding
// mode is currently active (*i.e.* whatever `as` would do).
assert_eq!(41.0f32.approx(), Ok(41u8));
assert_eq!(41.3f32.approx(), Ok(41u8));
assert_eq!(41.5f32.approx(), Ok(41u8));
assert_eq!(41.8f32.approx(), Ok(41u8));
assert_eq!(42.0f32.approx(), Ok(42u8));

assert_eq!(255.0f32.approx(), Ok(255u8));
assert_eq!(256.0f32.approx(), Err::<u8, _>(FloatError::PosOverflow(256.0)));

// Sometimes, it can be useful to saturate the conversion from float to
// integer directly, then account for NaN as input separately. The `Saturate`
// extension trait exists for this reason.
assert_eq!((-23.0f32).approx_as::<u8>().saturate(), Ok(0));
assert_eq!(302.0f32.approx_as::<u8>().saturate(), Ok(255u8));
assert!(std::f32::NAN.approx_as::<u8>().saturate().is_err());

// If you really don't care about the specific kind of error, you can just rely
// on automatic conversion to `GeneralErrorKind`.
fn too_many_errors() -> Result<(), GeneralErrorKind> {
    let x: u8 = 0u8.value_into()?;
    assert_eq!(x, 0);
    let y: i8 = 0u8.value_into()?;
    assert_eq!(y, 0);
    let z: i16 = 0u8.value_into()?;
    assert_eq!(z, 0);

    let x: u8 = 0.0f32.approx()?;
    assert_eq!(x, 0u8);
    Ok(())
}
too_many_errors().unwrap();

依赖关系

~15KB