1个不稳定发布
0.4.0-alpha.1 | 2023年12月29日 |
---|
#1104 在 Rust模式
70KB
1K SLoC
conv2
是由 Daniel Keep 编写的 conv
的分支。它正在更新以适应现代的 Rust 习惯。
此包提供了一组比 as
或 From
/Into
提供的更具体语义的转换特质。
这里提供的特质的目标是更具体地说明泛型代码可以依赖的内容,并提供对标准 From
/Into
特质的合理自描述替代方案。例如,虽然 T: From<U>
可能满足,但它对实现的 类型 没有任何限制。因此,此包中的特质试图非常具体地说明允许的转换类型。这使得它们更适用于特定情况,但在适用时更有用。
此外,From
/Into
要求所有转换都成功或崩溃。此包中定义的所有转换特质都有一个关联的错误类型,允许代码根据需要对失败的转换做出反应。
兼容性
conv2
与 Rust 1.56 及更高版本兼容。
概述
以下特质用于定义各种转换语义
ApproxFrom
- 近似转换,具有可选择的近似方案(见ApproxScheme
)。ValueFrom
- 精确、值保留的转换。
在定义转换时,尽可能实现*From
特质变体。在使用转换时,尽可能依赖*Into
特质变体。这是因为*Into
特质会自动使用*From
实现,但反过来则不行。实现*From
和使用*Into
可以确保转换在尽可能多的上下文中工作。
这些扩展方法提供了一些常见情况的帮助。
ConvUtil::approx_as
- 使用默认近似方案近似到Dst
。ConvUtil::approx_as_by
- 使用方案S
近似到Dst
。ConvUtil::into_as<Dst>
- 使用Into::into
转换为Dst
。ConvUtil::try_as<Dst>
- 使用TryInto::try_into
转换为Dst
。ConvUtil::value_as<Dst>
- 使用ValueInto::value_into
转换为Dst
。ConvAsUtil::approx
- 使用默认近似方案近似到推断的目标类型。ConvAsUtil::approx_by
- 使用方案S
近似到推断的目标类型。Saturate::saturate
- 在溢出时饱和。UnwrapOk::unwrap_ok
- 解包无法失败的结果。UnwrapOrInf::unwrap_or_inf
- 在失败时饱和到±∞。UnwrapOrInvalid::unwrap_or_invalid
- 在失败时用目标类型的“无效”哨兵值替换。UnwrapOrSaturate::unwrap_or_saturate
- 在失败时将值饱和到目标类型的最小或最大值。
提供的实现
该软件包提供了几个泛型实现
*From<A> for A
(所有类型都可以相互转换)。*Into<Dst> for Src where Dst: *From<Src>
(*From
实现隐含相应的*Into
实现)。
提供了内置数值类型(整数和浮点数)的转换。一般来说,除了 float → integer(这种转换通常不太可能 完全 成功)和 f64 → f32
(同样原因),所有数值对都存在 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>
,这使得错误可以通过?
运算符自动转换。实际上,所有错误都可以“展开”到所有更一般的格式(例如,NoError
→ NegOverflow
,PosOverflow
→ RangeError
→ FloatError
)。
除了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