isaacholt100 / bnum

Arbitrary, fixed size numeric types that extend the functionality of primitive numeric types in Rust.
https://crates.io/crates/bnum
Other
67 stars 8 forks source link

TryFrom impl for BUint <-> BUint, etc. #31

Closed softmoth closed 9 months ago

softmoth commented 9 months ago

I'm not sure what's technically possible. There's a distinct possibility I'm doing things wrong. But it would be nice to be able to convert between, say, U256 and U128. I may want to use one type for doing calculations, but store the result in a smaller type.

How to repeat

Here is one example.

type I1 = bnum::types::U256;
//type I2 = u32;
type I2 = bnum::types::U128;

fn main() -> Result<(), String> {
    let i1: I1 = I1::from(1u8);
    let i2: I2 = i1
        .try_into()
        .map_err(|_e| "type conversion error".to_owned())?;
    //let i2: I2 = workaround(i1).ok_or_else(|| "type conversion error".to_owned())?;
    println!("Converted {} into {}\n", i1, i2);
    Ok(())
}

fn workaround(i: I1) -> Option<I2> {
    use bnum::cast::As;
    if i <= I2::MAX.as_::<I1>() {
        Some(i.as_::<I2>())
    } else {
        None
    }
}
    Compiling testy v0.1.0 (/tmp/testy)
error[E0277]: the trait bound `BUint<2>: From<BUint<4>>` is not satisfied
 --> src/main.rs:8:10
  |
8 |         .try_into()
  |          ^^^^^^^^ the trait `From<BUint<4>>` is not implemented for `BUint<2>`
  |
  = help: the following other types implement trait `From<T>`:
            <BUint<N> as From<bool>>
            <BUint<N> as From<char>>
            <BUint<N> as From<usize>>
            <BUint<N> as From<u8>>
            <BUint<N> as From<u16>>
            <BUint<N> as From<u32>>
            <BUint<N> as From<u64>>
            <BUint<N> as From<u128>>
            <BUint<N> as From<[u64; N]>>
  = note: required for `BUint<4>` to implement `Into<BUint<2>>`
  = note: required for `BUint<2>` to implement `TryFrom<BUint<4>>`
  = note: required for `BUint<4>` to implement `TryInto<BUint<2>>`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `testy` (bin "testy") due to previous error

(1 of 19) error 277: the trait bound `BUint<2>: From<BUint<4>>` is not satisfied
isaacholt100 commented 9 months ago

Hi @softmoth, thanks very much for opening this issue! Apologies, I’m on holiday at the moment but when I’m back in 4 days time I’ll have a proper look.

isaacholt100 commented 9 months ago

Hi @softmoth, this is a good idea to add this functionality to the library, and you're right that the only way right now to convert between two BUint's or BInt's is casting. I will hopefully publish v0.9.0 in a few days' time, and will include this functionality in that version. Thanks!

isaacholt100 commented 9 months ago

@softmoth unfortunately TryFrom<BUint<M>> for BUint<N> can't be implemented, because it conflicts with the default implementation of TryFrom<BUint<N>> for BUint<N>, and there's no way of telling the compiler that M != N. However, I will create a new trait called BTryFrom which will be used for conversions between bnum integers. Hopefully there'll be a language feature allowing use to use good old regular TryFrom instead soon.

As an example, the following code fails to compile:

struct A<const N: usize>([u8; N]);

impl<const N: usize, const M: usize> TryFrom<A<N>> for A<M> {
// we would need to specify that N != M here
    type Error = ();

    fn try_from(f: A<N>) -> Result<Self, ()> {
        A([0; M])
    }
}
softmoth commented 9 months ago

I was hoping there was some compile time where expression that would do the trick. But it sounds like your workaround may still be useful. Thanks for looking at it!

isaacholt100 commented 9 months ago

No worries! I was thinking of a where expression too, but I think that would have to use generic_const_exprs, so unfortunately that would only work on nightly.

isaacholt100 commented 9 months ago

v0.9.0 has just been released, so closing this for now. Feel free to reopen when generic_const_exprs or another relevant feature is stabilised. Thanks!