1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
// https://www.apache.org/licenses/LICENSE-2.0
//! Traits
//!
//! This module only contains traits, allowing relatively safe glob-import:
//! ```
//! use easy_cast::traits::*;
//!
//! # fn main() {
//! let x = i32::conv_nearest(8.5);
//! let y: f32 = 12.cast();
//! # }
//! ```
use super::Result;
/// Like [`From`], but supports fallible conversions
///
/// This trait is intented to be an extension over [`From`], also supporting
/// fallible conversions of numeric types.
/// Since Rust does not yet have stable support for handling conflicting
/// implementations (specialization or otherwise), only conversions between
/// the most important numeric types are supported for now.
///
/// The sister-trait [`Cast`] supports "into" style usage.
pub trait Conv<T>: Sized {
/// Try converting from `T` to `Self`
///
/// This method must fail on inexact conversions.
fn try_conv(v: T) -> Result<Self>;
/// Convert from `T` to `Self`
///
/// This method must return the same result as [`Self::try_conv`] where that
/// method succeeds, but differs in the handling of errors:
///
/// - In debug builds the method panics on error
/// - Otherwise, the method may panic or may return a different value,
/// but like with the `as` keyword all results must be well-defined and
/// *safe*.
///
/// Default implementations use [`Self::try_conv`] and panic on error.
/// Implementations provided by this library will panic in debug builds
/// or if the `always_assert` feature flag is used, and otherwise will
/// behave identically to the `as` keyword.
///
/// This mirrors the behaviour of Rust's overflow checks on integer
/// arithmetic in that it is a tool for diagnosing logic errors where
/// success is expected.
fn conv(v: T) -> Self {
Self::try_conv(v).unwrap_or_else(|e| {
panic!("Conv::conv(_) failed: {}", e);
})
}
}
/// Like [`Into`], but for [`Conv`]
///
/// This trait is automatically implemented for every implementation of
/// [`Conv`].
pub trait Cast<T> {
/// Try converting from `Self` to `T`
///
/// Use this method to explicitly handle errors.
fn try_cast(self) -> Result<T>;
/// Cast from `Self` to `T`
///
/// Use this method *only* where success is expected: implementations are
/// permitted to panic or silently return a different (safe, defined) value
/// on error.
///
/// In debug builds, implementations must panic.
///
/// Implementations by this library will panic in debug builds or if the
/// `always_assert` feature flag is used, otherwise conversions have the
/// same behaviour as the `as` keyword.
fn cast(self) -> T;
}
impl<S, T: Conv<S>> Cast<T> for S {
#[inline]
fn cast(self) -> T {
T::conv(self)
}
#[inline]
fn try_cast(self) -> Result<T> {
T::try_conv(self)
}
}
/// Like [`From`], but for approximate numerical conversions
///
/// On success, the result must be approximately the same as the input value:
/// the difference must be smaller than the precision of the target type.
/// For example, one may have `i32::conv_approx(1.9f32) = 1` or
/// `f32::conv_approx(1f64 + (f32::EPSILON as f64) / 2.0) = 1.0`.
///
/// Precise rounding mode should usually be truncation (round towards zero),
/// but this is not required. Use [`ConvFloat`] where a specific rounding mode
/// is required.
///
/// The sister-trait [`CastApprox`] supports "into" style usage.
pub trait ConvApprox<T>: Sized {
/// Try converting from `T` to `Self`, allowing approximation of value
///
/// This conversion may truncate excess precision not supported by the
/// target type, so long as the *value* is approximately equal, from the
/// point of view of precision of the target type.
///
/// This method should allow approximate conversion, but fail on input not
/// (approximately) in the target's range.
fn try_conv_approx(x: T) -> Result<Self>;
/// Converting from `T` to `Self`, allowing approximation of value
///
/// This method must return the same result as [`Self::try_conv_approx`]
/// where that method succeeds, but differs in the handling of errors:
///
/// - In debug builds the method panics on error
/// - Otherwise, the method may panic or may return a different value,
/// but like with the `as` keyword all results must be well-defined and
/// *safe*.
///
/// Default implementations use [`Self::try_conv_approx`] and panic on error.
/// Implementations provided by this library will panic in debug builds
/// or if the `always_assert` feature flag is used, and otherwise will
/// behave identically to the `as` keyword.
///
/// This mirrors the behaviour of Rust's overflow checks on integer
/// arithmetic in that it is a tool for diagnosing logic errors where
/// success is expected.
#[inline]
fn conv_approx(x: T) -> Self {
Self::try_conv_approx(x).unwrap_or_else(|e| {
panic!("ConvApprox::conv_approx(_) failed: {}", e);
})
}
}
// TODO(specialization): implement also where T: ConvFloat<S>
impl<S, T: Conv<S>> ConvApprox<S> for T {
#[inline]
fn try_conv_approx(x: S) -> Result<Self> {
T::try_conv(x)
}
#[inline]
fn conv_approx(x: S) -> Self {
T::conv(x)
}
}
/// Like [`Into`], but for [`ConvApprox`]
///
/// On success, the result must be approximately the same as the input value:
/// the difference must be smaller than the precision of the target type.
/// For example, one may have `1.9f32.cast_approx() = 1`.
///
/// Precise rounding mode should usually be truncation (round towards zero),
/// but this is not required. Use [`CastFloat`] where a specific rounding mode
/// is required.
///
/// This trait is automatically implemented for every implementation of
/// [`ConvApprox`].
pub trait CastApprox<T> {
/// Try approximate conversion from `Self` to `T`
///
/// Use this method to explicitly handle errors.
fn try_cast_approx(self) -> Result<T>;
/// Cast approximately from `Self` to `T`
///
/// Use this method *only* where success is expected: implementations are
/// permitted to panic or silently return a different (safe, defined) value
/// on error.
///
/// In debug builds, implementations must panic.
///
/// Implementations by this library will panic in debug builds or if the
/// `always_assert` feature flag is used, otherwise conversions have the
/// same behaviour as the `as` keyword.
fn cast_approx(self) -> T;
}
impl<S, T: ConvApprox<S>> CastApprox<T> for S {
#[inline]
fn try_cast_approx(self) -> Result<T> {
T::try_conv_approx(self)
}
#[inline]
fn cast_approx(self) -> T {
T::conv_approx(self)
}
}
/// Nearest / floor / ceiling conversions from floating point types
///
/// This trait is explicitly for conversions from floating-point values to
/// integers, supporting four rounding modes.
///
/// As with [`Conv`], the `try_conv_*` methods must be implemented and must fail
/// if conversion to the expected value is not possible. If the source is non-
/// finite (`inf` or `NaN`), then `Error::Range` should be returned.
///
/// The `conv_*` methods each have a default implementation over the `try_..`
/// variant which panics on failure. Implementations handle errors as follows:
///
/// - In debug builds, the methods must panic
/// - Otherwise, the method may panic or may return a different value; all
/// results must be well-defined and *safe*.
/// - Implementations provided by this library will also panic if the
/// `always_assert` or `assert_float` feature flag is used.
///
/// The sister-trait [`CastFloat`] supports "into" style usage.
#[cfg(any(feature = "std", feature = "libm"))]
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "libm"))))]
pub trait ConvFloat<T>: Sized {
/// Try converting to integer with truncation
///
/// Rounds towards zero (same as `as`).
fn try_conv_trunc(x: T) -> Result<Self>;
/// Try converting to the nearest integer
///
/// Half-way cases are rounded away from `0`.
fn try_conv_nearest(x: T) -> Result<Self>;
/// Try converting the floor to an integer
///
/// Returns the largest integer less than or equal to `x`.
fn try_conv_floor(x: T) -> Result<Self>;
/// Try convert the ceiling to an integer
///
/// Returns the smallest integer greater than or equal to `x`.
fn try_conv_ceil(x: T) -> Result<Self>;
/// Convert to integer with truncatation
///
/// Rounds towards zero (same as `as`).
fn conv_trunc(x: T) -> Self {
Self::try_conv_trunc(x).unwrap_or_else(|e| panic!("ConvFloat::conv_trunc(_) failed: {}", e))
}
/// Convert to the nearest integer
///
/// Half-way cases are rounded away from `0`.
fn conv_nearest(x: T) -> Self {
Self::try_conv_nearest(x)
.unwrap_or_else(|e| panic!("ConvFloat::conv_nearest(_) failed: {}", e))
}
/// Convert the floor to an integer
///
/// Returns the largest integer less than or equal to `x`.
fn conv_floor(x: T) -> Self {
Self::try_conv_floor(x).unwrap_or_else(|e| panic!("ConvFloat::conv_floor(_) failed: {}", e))
}
/// Convert the ceiling to an integer
///
/// Returns the smallest integer greater than or equal to `x`.
fn conv_ceil(x: T) -> Self {
Self::try_conv_ceil(x).unwrap_or_else(|e| panic!("ConvFloat::conv_ceil(_) failed: {}", e))
}
}
/// Like [`Into`], but for [`ConvFloat`]
///
/// Use:
///
/// - `try_cast_*` methods to explicitly handle errors
/// - `cast_*` methods *only* where success is expected. Implementations are
/// permitted to panic or silently return a different (safe, defined) value
/// on error.
///
/// In debug builds, implementations must panic.
///
/// Implementations by this library will panic in debug builds or if the
/// `always_assert` or `assert_float` feature flag is used, otherwise
/// conversions have similar behaviour to the `as` keyword.
///
/// This trait is automatically implemented for every implementation of
/// [`ConvFloat`].
#[cfg(any(feature = "std", feature = "libm"))]
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "std", feature = "libm"))))]
pub trait CastFloat<T> {
/// Cast to integer, truncating
///
/// Rounds towards zero (same as `as`).
fn cast_trunc(self) -> T;
/// Cast to the nearest integer
///
/// Half-way cases are rounded away from `0`.
fn cast_nearest(self) -> T;
/// Cast the floor to an integer
///
/// Returns the largest integer less than or equal to `self`.
fn cast_floor(self) -> T;
/// Cast the ceiling to an integer
///
/// Returns the smallest integer greater than or equal to `self`.
fn cast_ceil(self) -> T;
/// Try converting to integer with truncation
///
/// Rounds towards zero (same as `as`).
fn try_cast_trunc(self) -> Result<T>;
/// Try converting to the nearest integer
///
/// Half-way cases are rounded away from `0`.
fn try_cast_nearest(self) -> Result<T>;
/// Try converting the floor to an integer
///
/// Returns the largest integer less than or equal to `x`.
fn try_cast_floor(self) -> Result<T>;
/// Try convert the ceiling to an integer
///
/// Returns the smallest integer greater than or equal to `x`.
fn try_cast_ceil(self) -> Result<T>;
}
#[cfg(any(feature = "std", feature = "libm"))]
impl<S, T: ConvFloat<S>> CastFloat<T> for S {
#[inline]
fn cast_trunc(self) -> T {
T::conv_trunc(self)
}
#[inline]
fn cast_nearest(self) -> T {
T::conv_nearest(self)
}
#[inline]
fn cast_floor(self) -> T {
T::conv_floor(self)
}
#[inline]
fn cast_ceil(self) -> T {
T::conv_ceil(self)
}
#[inline]
fn try_cast_trunc(self) -> Result<T> {
T::try_conv_trunc(self)
}
#[inline]
fn try_cast_nearest(self) -> Result<T> {
T::try_conv_nearest(self)
}
#[inline]
fn try_cast_floor(self) -> Result<T> {
T::try_conv_floor(self)
}
#[inline]
fn try_cast_ceil(self) -> Result<T> {
T::try_conv_ceil(self)
}
}