Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting to a generic type

Tags:

rust

I have a newbie question about generics in Rust (version 1.0).

Let's say I write a generic function to do division. Never mind the usefulness of such a function; it's a simple function to keep this question simple.

fn divide<T: std::ops::Div>(a: T, b: T) -> T {
    a / b
}

fn main() {
    println!("{}", divide(42, 18))
}

This program fails to compile.

src/main.rs:2:5: 2:10 error: mismatched types:
 expected `T`,
    found `<T as core::ops::Div>::Output`
(expected type parameter,
    found associated type) [E0308]
src/main.rs:2     a / b
                  ^~~~~

I understand that the compiler error is telling me that the result of the division operation is type Output, not T, and I see the Output type in the standard library documentation.

How do I convert from Output to T? I try to use as to cast.

fn divide<T: std::ops::Div>(a: T, b: T) -> T {
    (a / b) as T
}

fn main() {
    println!("{}", divide(42, 18))
}

This causes a different compiler error.

src/main.rs:2:5: 2:17 error: non-scalar cast: `<T as core::ops::Div>::Output` as `T`
src/main.rs:2     (a / b) as T
                  ^~~~~~~~~~~~

I'm out of ideas to make this work, and I realize I lack understanding of something fundamental about the language here, but I don't even know what to look for to make this work. Help?

like image 754
Craig M. Brandenburg Avatar asked Jun 19 '15 15:06

Craig M. Brandenburg


People also ask

Can you cast generic type java?

You can cast one instance of a generic class into another only if the two are otherwise compatible and their type arguments are the same.

What is generic type?

A generic type is a generic class or interface that is parameterized over types. The following Box class will be modified to demonstrate the concept.

Do generics prevent type cast errors?

Implementing generics into your code can greatly improve its overall quality by preventing unprecedented runtime errors involving data types and typecasting.


2 Answers

You simply have to specify T::Output as the return type of the function:

fn divide<T: std::ops::Div>(a: T, b: T) -> T::Output {
    a / b
}

Edit to add more explanation on why you cannot do the cast inside the function

When you are IN your generic function divide, the compiler yet doesn't know that you can cast T to T::Output, so the cast is invalid. They are generic types, they can be anything, how the compiler knows that you can cast from T to T::Output ?

a / b produces something of type T::Output, so in the solution above there is not cast, T::Output is simply the right type.

Edit to add another possible solution using std::convert::From

The most (I think) generic implementation is when you know that the cast from T::Output to T is possible. You can bound T to implement From for T::Output. This is a complete example:

use std::ops::Div;
use std::convert::From;

fn divide<T: Div>(a: T, b: T) -> T
    where T: From<<T as Div>::Output>
{
    T::from(a / b)
}

#[derive(Debug)]
struct Bip(u32);

impl Div for Bip {
    type Output = f32;

    fn div(self, rhs: Bip) -> f32 {
        (self.0 / rhs.0) as f32
    }
}

impl From<f32> for Bip {
    fn from(value: f32) -> Self {
        Bip(value as u32)
    }
}

fn main() {
    println!("{:?}", divide(12, 4));
    println!("{:?}", divide(Bip(12), Bip(4)));
}
like image 128
eulerdisk Avatar answered Sep 18 '22 22:09

eulerdisk


Andreas' answer explains why the cast is impossible, and that making the function as generic as possible solves this issue.

It is not, however, the only solution. Rust also supports the ability to constrain the associated types (Output here) of a generic function.

An alternative would be:

use std::ops::Div;

fn divide<T>(a: T, b: T) -> T
    where T: Div<Output = T>
{
    a / b
}

The <Output = T> bit instructs the compiler to only accept in this functions types T for which the implementation of Div has an Output equal to T. This is obviously more restricting, but it allows ensuring that the result of divide is of type T.

like image 24
Matthieu M. Avatar answered Sep 18 '22 22:09

Matthieu M.