Trait based generics is hard for a simple problem?

SICP problem 1.3, it's a very trivial problem usually taught in CS101 first/second week in the uni. The problem states thusly:

Declare a function that takes three numbers as arguments and returns the sum of the squares
of the two larger numbers.

I find it embarrassing that I cannot implement this function:

A non-generic approach would look something like this:

/// Declare a function that takes three numbers as arguments and returns the sum of the squares
/// of the two larger numbers.
fn e3(n1: f32, n2: f32, n3: f32) -> f32 {
    match (n1 <= n2, n1 <= n3, n2 <= n1, n2 <= n3) {
        (true, true, _, _) => n2 * n2 + n3 * n3,
        (_, _, true, true) => n1 * n1 + n3 * n3,
        _ => n1 * n1 + n2 * n2,
    }
}


#[cfg(test)]
mod test {
    #[test]
    fn e3() {
        assert_eq!(super::e3(2., 1., 3.), 13.);
    }
}

easy but it only works if all the parameters happen to be f32. While writing a generic solution I'm drowning in the rigidity of the type constraints

fn g_e3<T, U, V, R>(n1: T, n2: U, n3: V) -> R
where
    T: std::cmp::PartialOrd + std::ops::Mul + std::convert::From<U> + std::convert::From<V>,
    U: std::cmp::PartialOrd + std::ops::Mul + std::convert::From<T> + std::convert::From<V>,
    V: std::cmp::PartialOrd + std::ops::Mul + std::convert::From<T> + std::convert::From<U>,
    <T as std::ops::Mul>::Output: std::ops::Add,
    <U as std::ops::Mul>::Output: std::ops::Add,
{
    match (
        n1 <= n2.into(),
        n1 <= n3.into(),
        n2 <= n1.into(),
        n2 <= n3.into(),
    ) {
        (true, true, _, _) => n2 * n2 + n3 * n3,
        (_, _, true, true) => n1 * n1 + n3 * n3,
        _ => n1 * n1 + n2 * n2,
    }
}

This won't work for obvious reasons. Whenever I fix something, another issue appears. Is there a way to have the compiler automatically deduce the constraints? Like in D:

T4 e3(T1, T2, T3, T4)(const T1 n1, const T2 n2, const T3 n3)
{
    return n1 * n1 + n2 * n2 + n3 * n3 - ((n1 <= n2 && n1 <= n3) ? n1 * n1
            : (n2 <= n1 && n2 <= n3) ? n2 * n2 : n3 * n3);
}

would this work?

There is no way for the compiler to auto-deduce the bounds because Rust wants all interfaces to be as explicit as possible (this means bounds must be written out)

1 Like

Output = O

Ok that's a very good idea, I was going through all kinds of trouble defining R

This however, won't work with different data types like e3(f32, i32, i32).

.. Honestly I don't think that's a simple and practical problem. Why should we provide a fn that accepts three possibly different types of values and compare them? The i32 and f32 cannot be compared using PartialEq without conversion anyway.

1 Like

There isn't a general way to do that in Rust. If you want it to work, you will have to provide a way to convert between all of the types (via traits or conversion functions), or convert all the types to a common type before hand.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.