General casting

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

Say I'm writing a generic division function. Do not pay attention to the usefulness of such a function; this is 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 does not 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 a compiler error tells me that the result of the division operation is the Output type, not T , and I see the Output type in the standard library documentation .

How do I convert from Output to T ? I am trying to use as to create.

 fn divide<T: std::ops::Div>(a: T, b: T) -> T { (a / b) as T } fn main() { println!("{}", divide(42, 18)) } 

This leads to another 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 have no ideas to do this work, and I understand that I lack an understanding of something fundamental in this language, but I don’t even know what to look for in order to do this work. Help?

+6
source share
2 answers

You just need 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 explanations about why you cannot roll inside a function.

When you are in your division of a common function, the compiler does not yet know that you can use T for T::Output , so the cast is not valid. They are typical types, they can be any, how does the compiler know that you can use from T to T::Output ?

a / b creates something like T::Output , so there is no cast in the solution above, T::Output is just the right type.

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

The most (I think) common implementation is when you know that casting from T::Output to T is possible. You can bind T to the From implementation 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))); } 
+9
source

Andreas's answer explains why casting is not possible, and that performing this function as universally as possible solves this problem.

This, however, is not the only solution. Rust also supports the ability to restrict the associated types ( Output here) of a common function.

An alternative could 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 accept <Output = T> only for these types of functions, for which the implementation of Div has an Output equal to T This, obviously, is more restrictive, but allows us to guarantee that the result of the divide is of type T

+4
source

Source: https://habr.com/ru/post/989417/


All Articles