The code below works for me but if I remove the comments around my generic version of push_3, I get the compile error.
It seems to me that rust is complaining that 3.0 is not of the proper type in the generic function but it works fine for the non-generic version and I do not understand why ?
use std::cell::RefCell;
use std::ops::DerefMut;
fn push_3(v : &mut Vec<f32>) {
v.push( 3.0 );
}
/*
fn generic_push_3<T>(v : &mut Vec<T>) {
v.push( 3.0 );
}
*/
fn main() {
let v = RefCell::new( vec![1.0, 2.0] );
push_3( v.borrow_mut( ).deref_mut() );
println!( "{:?}", v );
}
Here, the caller decides what T is, so generic_push_3 must work no matter what the caller chooses. What should this code do if I call generic_push_3::<String>? You're going to add 3.0 to a vector of strings?
Because with generic functions the caller will decide what T is going to be. So you cannot generally assume that it's going to be a float literal. Whatever is going to be pushed onto the Vec<T> needs to be of type T.
Since, when writing the generic function, you cannot know which concrete type T will be when the function is called, the only thing you can do within the function body is to rely on trait bounds that T satisfies, e.g.:
Notice, that we needed to limit the types that T can be to those implementing the Default trait by means of the where clause, so that we may assume, that T has a default() method to construct an instance of T.
C++ doesn't have generics at all, it only have templates – thus it's useless to say what C++ does. It's entirely different thing which is only very superficially similar to generics.
Ada, Java, C#, Go, Haskell and all other languages that do have generics work more-or-less like Rust does.
No, because there are not “integer value 3” type. You may ask for a type to be constructible from i8 (i8: Into<T>) – but then u8 wouldn't be supported. Or you may ask for a type to be constructible from u8(i8: Into<T>) – but then i8 wouldn't be supported.
It's known that writing generic numerical computation code in Rust is real PITA, people usually just go with macros to implement them and not with generics.
It's actually a {float} value - either f32 or f64 - since the value is 3.0 and not 3.
In either case, though, yes, there is:
fn generic_push_3<T>(v: &mut Vec<T>)
where T: From<f64> {
v.push(T::from(3.0));
}
You can also write the innermost conversion as (3.0).into(), if you prefer; Into has a blanket implementation based on From.
For complicated reasons, there isn't a numeric tower of types in Rust, so there's no type or trait that is implemented by all integers or by all floating point types. There are crates that provide that, if you want something more general, but working with the specific numeric type of interest, non-generically, is usually good enough.