Skip to content

Commit

Permalink
Merge #123
Browse files Browse the repository at this point in the history
123: Add TryFrom impls r=cuviper a=milesand

Closes #120 

Currently contains:

*  `TryFrom` translations of existing conversion traits.
* A minimal Error type used for all `TryFrom` implementations.

Additional implemetations (e.g. `TryFrom<BigInt> for BigUint` which skips `clone()`) could be beneficial. Also, no new tests have been added since all new impls are basically thin adapters around existing, tested methods. It may be beneficial to add them regardless.

Co-authored-by: Jewoo Lee <shema7k@gmail.com>
Co-authored-by: Josh Stone <cuviper@gmail.com>
  • Loading branch information
3 people authored Mar 13, 2020
2 parents 75c3e07 + 029d04d commit c076bb8
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ sudo: false
rust:
- 1.31.0 # 2018!
- 1.32.0 # rand
- 1.34.0 # quickcheck
- 1.34.0 # quickcheck, has_try_from
- 1.36.0 # alloc
- stable
- beta
Expand Down
4 changes: 4 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ fn main() {
if pointer_width.as_ref().map(String::as_str) == Ok("64") {
autocfg::emit("u64_digit");
}
let ac = autocfg::new();
if ac.probe_path("std::convert::TryFrom") || ac.probe_path("core::convert::TryFrom") {
autocfg::emit("has_try_from");
}

autocfg::rerun_path("build.rs");

Expand Down
68 changes: 67 additions & 1 deletion src/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use crate::std_alloc::Box;
use crate::std_alloc::{String, Vec};
use core::cmp::Ordering::{self, Equal, Greater, Less};
#[cfg(has_try_from)]
use core::convert::TryFrom;
use core::default::Default;
use core::fmt;
use core::hash;
Expand All @@ -29,11 +31,13 @@ use num_traits::{

use self::Sign::{Minus, NoSign, Plus};

use super::ParseBigIntError;
use crate::big_digit::{self, BigDigit, DoubleBigDigit};
use crate::biguint;
use crate::biguint::to_str_radix_reversed;
use crate::biguint::{BigUint, IntDigits};
use crate::ParseBigIntError;
#[cfg(has_try_from)]
use crate::TryFromBigIntError;

use crate::IsizePromotion;
use crate::UsizePromotion;
Expand Down Expand Up @@ -2442,6 +2446,44 @@ impl ToPrimitive for BigInt {
}
}

macro_rules! impl_try_from_bigint {
($T:ty, $to_ty:path) => {
#[cfg(has_try_from)]
impl TryFrom<&BigInt> for $T {
type Error = TryFromBigIntError<()>;

#[inline]
fn try_from(value: &BigInt) -> Result<$T, TryFromBigIntError<()>> {
$to_ty(value).ok_or(TryFromBigIntError::new(()))
}
}

#[cfg(has_try_from)]
impl TryFrom<BigInt> for $T {
type Error = TryFromBigIntError<BigInt>;

#[inline]
fn try_from(value: BigInt) -> Result<$T, TryFromBigIntError<BigInt>> {
<$T>::try_from(&value).map_err(|_| TryFromBigIntError::new(value))
}
}
};
}

impl_try_from_bigint!(u8, ToPrimitive::to_u8);
impl_try_from_bigint!(u16, ToPrimitive::to_u16);
impl_try_from_bigint!(u32, ToPrimitive::to_u32);
impl_try_from_bigint!(u64, ToPrimitive::to_u64);
impl_try_from_bigint!(usize, ToPrimitive::to_usize);
impl_try_from_bigint!(u128, ToPrimitive::to_u128);

impl_try_from_bigint!(i8, ToPrimitive::to_i8);
impl_try_from_bigint!(i16, ToPrimitive::to_i16);
impl_try_from_bigint!(i32, ToPrimitive::to_i32);
impl_try_from_bigint!(i64, ToPrimitive::to_i64);
impl_try_from_bigint!(isize, ToPrimitive::to_isize);
impl_try_from_bigint!(i128, ToPrimitive::to_i128);

impl FromPrimitive for BigInt {
#[inline]
fn from_i64(n: i64) -> Option<BigInt> {
Expand Down Expand Up @@ -2667,6 +2709,30 @@ impl biguint::ToBigUint for BigInt {
}
}

#[cfg(has_try_from)]
impl TryFrom<&BigInt> for BigUint {
type Error = TryFromBigIntError<()>;

#[inline]
fn try_from(value: &BigInt) -> Result<BigUint, TryFromBigIntError<()>> {
value.to_biguint().ok_or(TryFromBigIntError::new(()))
}
}

#[cfg(has_try_from)]
impl TryFrom<BigInt> for BigUint {
type Error = TryFromBigIntError<BigInt>;

#[inline]
fn try_from(value: BigInt) -> Result<BigUint, TryFromBigIntError<BigInt>> {
if value.sign() == Sign::Minus {
Err(TryFromBigIntError::new(value))
} else {
Ok(value.data)
}
}
}

macro_rules! impl_to_bigint {
($T:ty, $from_ty:path) => {
impl ToBigInt for $T {
Expand Down
63 changes: 63 additions & 0 deletions src/biguint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use crate::std_alloc::Box;
use crate::std_alloc::{Cow, String, Vec};
use core::cmp;
use core::cmp::Ordering::{self, Equal, Greater, Less};
#[cfg(has_try_from)]
use core::convert::TryFrom;
use core::default::Default;
use core::fmt;
use core::hash;
Expand Down Expand Up @@ -43,6 +45,8 @@ use self::monty::monty_modpow;
use crate::UsizePromotion;

use crate::ParseBigIntError;
#[cfg(has_try_from)]
use crate::TryFromBigIntError;

#[cfg(feature = "quickcheck")]
use quickcheck::{Arbitrary, Gen};
Expand Down Expand Up @@ -1929,6 +1933,44 @@ impl ToPrimitive for BigUint {
}
}

macro_rules! impl_try_from_biguint {
($T:ty, $to_ty:path) => {
#[cfg(has_try_from)]
impl TryFrom<&BigUint> for $T {
type Error = TryFromBigIntError<()>;

#[inline]
fn try_from(value: &BigUint) -> Result<$T, TryFromBigIntError<()>> {
$to_ty(value).ok_or(TryFromBigIntError::new(()))
}
}

#[cfg(has_try_from)]
impl TryFrom<BigUint> for $T {
type Error = TryFromBigIntError<BigUint>;

#[inline]
fn try_from(value: BigUint) -> Result<$T, TryFromBigIntError<BigUint>> {
<$T>::try_from(&value).map_err(|_| TryFromBigIntError::new(value))
}
}
};
}

impl_try_from_biguint!(u8, ToPrimitive::to_u8);
impl_try_from_biguint!(u16, ToPrimitive::to_u16);
impl_try_from_biguint!(u32, ToPrimitive::to_u32);
impl_try_from_biguint!(u64, ToPrimitive::to_u64);
impl_try_from_biguint!(usize, ToPrimitive::to_usize);
impl_try_from_biguint!(u128, ToPrimitive::to_u128);

impl_try_from_biguint!(i8, ToPrimitive::to_i8);
impl_try_from_biguint!(i16, ToPrimitive::to_i16);
impl_try_from_biguint!(i32, ToPrimitive::to_i32);
impl_try_from_biguint!(i64, ToPrimitive::to_i64);
impl_try_from_biguint!(isize, ToPrimitive::to_isize);
impl_try_from_biguint!(i128, ToPrimitive::to_i128);

impl FromPrimitive for BigUint {
#[inline]
fn from_i64(n: i64) -> Option<BigUint> {
Expand Down Expand Up @@ -2034,6 +2076,27 @@ impl_biguint_from_uint!(u16);
impl_biguint_from_uint!(u32);
impl_biguint_from_uint!(usize);

macro_rules! impl_biguint_try_from_int {
($T:ty, $from_ty:path) => {
#[cfg(has_try_from)]
impl TryFrom<$T> for BigUint {
type Error = TryFromBigIntError<()>;

#[inline]
fn try_from(value: $T) -> Result<BigUint, TryFromBigIntError<()>> {
$from_ty(value).ok_or(TryFromBigIntError::new(()))
}
}
};
}

impl_biguint_try_from_int!(i8, FromPrimitive::from_i8);
impl_biguint_try_from_int!(i16, FromPrimitive::from_i16);
impl_biguint_try_from_int!(i32, FromPrimitive::from_i32);
impl_biguint_try_from_int!(i64, FromPrimitive::from_i64);
impl_biguint_try_from_int!(isize, FromPrimitive::from_isize);
impl_biguint_try_from_int!(i128, FromPrimitive::from_i128);

/// A generic trait for converting a value to a `BigUint`.
pub trait ToBigUint {
/// Converts the value of `self` to a `BigUint`.
Expand Down
44 changes: 44 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,50 @@ impl Error for ParseBigIntError {
}
}

/// The error type returned when a checked conversion regarding big integer fails.
#[cfg(has_try_from)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct TryFromBigIntError<T> {
original: T,
}

#[cfg(has_try_from)]
impl<T> TryFromBigIntError<T> {
fn new(original: T) -> Self {
TryFromBigIntError { original }
}

fn __description(&self) -> &str {
"out of range conversion regarding big integer attempted"
}

/// Extract the original value, if available. The value will be available
/// if the type before conversion was either [`BigInt`] or [`BigUint`].
///
/// [`BigInt`]: struct.BigInt.html
/// [`BigUint`]: struct.BigUint.html
pub fn into_original(self) -> T {
self.original
}
}

#[cfg(all(feature = "std", has_try_from))]
impl<T> std::error::Error for TryFromBigIntError<T>
where
T: fmt::Debug,
{
fn description(&self) -> &str {
self.__description()
}
}

#[cfg(has_try_from)]
impl<T> fmt::Display for TryFromBigIntError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.__description().fmt(f)
}
}

pub use crate::biguint::BigUint;
pub use crate::biguint::ToBigUint;

Expand Down

0 comments on commit c076bb8

Please sign in to comment.