Ganesh11
Ganesh11

Reputation: 129

Mismatched types parameter error on rust generics

expected type parameter T, found type parameter A error display. I have written lifetime implementation code also but it stills doesn't solve the problem. What's wrong I am doing?

fn main() {
    let x = 3;
    let y = 5.0;
    let max_value = max(x, y);
    println!("The maximum value is {}", max_value); 
}

fn max<T: PartialOrd, A: PartialOrd>(x: T, y: A) -> T {
    if x > y {
        x
    } else {
        y
    }
}

// fn main() {
//     let x = 3;
//     let y = 5.0;
//     let max_value = max(&x, &y);
//     println!("The maximum value is {}", max_value); 
// }

// fn max<'a, T: PartialOrd + Copy, A: PartialOrd + Copy>(x: &'a T, y: &'a A) -> &'a T {
//     if x > y {
//         x
//     } else {
//         y
//     }
// }

Upvotes: 2

Views: 223

Answers (1)

cdhowie
cdhowie

Reputation: 169008

T and A do not have to be the same type, so you have two problems.

The first is that you constrain T and A to be PartialOrd, which is the same thing as PartialOrd<Self>. So your actual constraints are T: PartialOrd<T>, A: PartialOrd<A>. This means you can compare the order of T's to other T's and A's to other A's, but x > y compares a T to an A.

Instead, you need to constrain T: PartialOrd<A>. (This also fails, but because of the invocation in main() -- more on that later.)

Second, the function is declared to return T but the else block returns y, which is not a T. Rust is statically typed, so it expects the types to exactly match.

This could be fixed by requiring that A can be converted to T (that is, A: Into<T>) and then you can return y.into() from the else block.

So at this point, we have:

fn main() {
    let x = 3;
    let y = 5.0;
    let max_value = max(x, y);
    println!("The maximum value is {}", max_value); 
}

fn max<T: PartialOrd<A>, A: Into<T>>(x: T, y: A) -> T {
    if x > y {
        x
    } else {
        y.into()
    }
}

But now you are left with more problems:

  • There are no types T and A satisfying T: PartialOrd<A> where T is an integer and A is a float, therefore you cannot call this function with 3 and 5.0 as you do in main().
  • Likewise, there's no implementation of Into<T> on A for an integer type T and a float type A.
  • x > y will move x and y, and then you cannot return them later. This is trivially fixed by constraining both T and A to be Copy.

The second issue could be fixed by having an enum that means "either T or A" and returning that instead. The either crate has such a type called Either, which we can use here as Either<T, A>:

use either::Either;

fn main() {
    let x = 3;
    let y = 5.0;
    let max_value = max(x, y);
    println!("The maximum value is {}", max_value); 
}

fn max<T: PartialOrd<A> + Copy, A: Copy>(x: T, y: A) -> Either<T, A> {
    if x > y {
        Either::Left(x)
    } else {
        Either::Right(y)
    }
}

(The println! works because Either<T, A> implements Display when both T and A do.)

You are still left with the problem where there's no built-in ordering implementation between integers and floats.

A "hail mary" solution could be to require that T and A can both be converted to f64 and then convert x and y to f64 before comparing them:

use either::Either;

fn main() {
    let x = 3;
    let y = 5.0;
    let max_value = max(x, y);
    println!("The maximum value is {}", max_value); 
}

fn max<T: Copy + Into<f64>, A: Copy + Into<f64>>(x: T, y: A) -> Either<T, A> {
    if x.into() > y.into() {
        Either::Left(x)
    } else {
        Either::Right(y)
    }
}

This is the first bit of code we have that actually compiles, and this might be good enough for your purposes. There are still some issues that remain, however:

  • i64 and u64 cannot be losslessy converted to f64, therefore they do not implement Into<f64>, and so if you change let x = 3; to let x = 3u64; (or 3i64) compilation will again fail.
  • f64 does not implement Ord because it's possible for there to be two f64 values x and y that are not equal but neither is greater than the other -- if either value is NaN, for example. This won't cause your program to crash, but it may produce an unexpected or incorrect result.

I suspect that this is a learning exercise, so hopefully this answer helps you understand what is wrong with the original code. I would not recommend a function like this in a real-world program; instead, it would be far better to convert both arguments to be of the same Ord-implementing type ahead of time and then you can use the built-in std::cmp::max function (or Ord::max).

Upvotes: 4

Related Questions