rust-num / num-traits

Numeric traits for generic mathematics in Rust
Apache License 2.0
694 stars 131 forks source link

`struct` built on top of `pub trait Num` can't compile if `pub trait Num` doesn't impl `Debug` #318

Open hamirmahal opened 4 months ago

hamirmahal commented 4 months ago
#[test]
fn test_debug_trait_impl() {
    use num_traits::Num;
    use std::ops::Add;
    pub struct LengthNArrayOfTypeT<const N: usize, T: Num>([T; N]);
    impl<const N: usize, T: Num + Clone> Add for LengthNArrayOfTypeT<N, T> {
        type Output = Self;
        fn add(self, rhs: Self) -> Self {
            LengthNArrayOfTypeT(
                self.0
                    .iter()
                    .zip(rhs.0.iter())
                    .map(|(a, b)| a.clone() + b.clone())
                    .collect::<Vec<T>>()
                    .try_into()
                    .unwrap(),
            )
        }
    }
}

is an example of code that can't compile because of this issue.

cuviper commented 4 months ago

You should add T: Debug if you need it, just as you have for Clone. The compiler even suggests that!

help: consider further restricting this bound
   |
6  |     impl<const N: usize, T: Num + Clone + std::fmt::Debug> Add for LengthNArrayOfTypeT<N, T> {
   |                                         +++++++++++++++++

The place your example encounters this doesn't have anything to do with being a numeric type. The error is from your Vec<T> to [T; N] conversion .try_into().unwrap(), which wants to debug-print its potential Result::Err (containing the original value) when unwrapped. You could make that .try_into().ok().unwrap() to get an Option unwrap instead, especially since you know in this case that the length will never actually mismatch.

Or you could rewrite this to update the value in-place:

        fn add(mut self, rhs: Self) -> Self {
            for (a, b) in std::iter::zip(&mut self.0, rhs.0) {
                *a = a.clone() + b;
            }
            self
        }

And with T: AddAssign (or T: NumAssign), you wouldn't even need the clone.

hamirmahal commented 4 months ago

The compiler even suggests that!

I know. I started needing to add it everywhere, so I started looking into different approaches.

image

The error is from your Vec<T> to [T; N] conversion .try_into().unwrap(), which wants to debug-print its potential Result::Err (containing the original value) when unwrapped. You could make that .try_into().ok().unwrap() to get an Option unwrap instead, especially since you know in this case that the length will never actually mismatch.

Thank you for the suggestion.

cuviper commented 4 months ago

You could define your own extended trait:

trait MyNum: num_traits::Num + Clone + Debug {}
impl<T> MyNum for T where T: num_traits::Num + Clone + Debug {}