So I did manage to make a generic function where I can use the default types:
fn arithmetic_operation<T, F>(a: T, b: T, operation: F) -> T
where
T: Copy,
F: Fn(T, T) -> T,
{
operation(a, b)
}
fn main() {
// f64
let result_f64 = arithmetic_operation(2.0, 3.0 , |x, y| x+y);
println!("Result f64: {:?}", result_f64);
// Result f64: 5.0
// i128
let result_i128 = arithmetic_operation(2i128, 3i128 , |x, y| x+y);
println!("Result i128: {:?}", result_i128);
// Result i128: 5
}
I would like to create some custom types (like money, km, ...) and still use generic functions. But I can't figure out how to write a function signature for custom types.
If I write a custom type defined with an enum:
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum ResType {
Int(i128),
Float(f64),
}
impl ResType {
fn get_i128(self) -> i128 {
match self {
ResType::Int(val) => val,
ResType::Float(val) => val as i128
}
}
fn get_f64(self) -> f64 {
match self {
ResType::Int(val) => val as f64,
ResType::Float(val) => val
}
}
fn is_float(&self) -> bool {
matches!(self, ResType::Float(_) )
}
}
fn arithmetic_operation<T, F>(a: ResType, b: ResType, func: F) -> ResType
where
T: Copy,
F: Fn(T, T) -> T,
{
if a.is_float() || b.is_float() {
let res = func(a.get_f64(), b.get_f64());
return ResType::Float(res);
}
let res = func(a.get_i128(), b.get_i128());
ResType::Int(res)
}
fn main() {
let a = ResType::Int(42);
let b = ResType::Int(13);
println!("Result ResType: {:?}", arithmetic_operation(a, b, |x,y| x+y));
}
I have some errors explaining that a T is required, but an f64 is given.
note: expected type parameter `T`, found `f64`
--> src/main.rs:33:24
|
33 | let res = func(a.get_f64(), b.get_f64());
| ^^^^^^^^^^^
= note: expected type parameter `T`
found type `f64`
How can I make this type of generic function work with a custom type ? Or is this a wrong approach altogether to create new types ?
You cannot add two factors of type ResType
.
Instead, you have to consider how to link the variants of ResType
.
Better is to implement the trait From
for f64
and i128
instead of the homebrewed functions get_f64()
and get_i128()
.
There are traits for the most arithmetic functions in Rust, e.g. Add
that you could implement for your ResType
.
Here is a working version of your program:
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum ResType {
Int(i128),
Float(f64),
}
impl From<ResType> for i128 {
fn from(rt: ResType) -> i128 {
match rt {
ResType::Int(val) => val,
ResType::Float(val) => val as i128, // loss of accuracy!
}
}
}
impl From<ResType> for f64 {
fn from(rt: ResType) -> f64 {
match rt {
ResType::Int(val) => val as f64,
ResType::Float(val) => val,
}
}
}
fn arithmetic_operation(a: ResType, b: ResType, func: fn(ResType, ResType) -> ResType) -> ResType {
(func)(a, b)
}
fn main() {
let a = ResType::Int(42);
let b = ResType::Int(13);
println!(
"Result ResType: {:?}",
arithmetic_operation(a, b, |a: ResType, b: ResType| {
match a {
ResType::Int(i) => {
// loss of accuracy if b is Float!
ResType::Int(i + i128::from(b))
}
ResType::Float(f) => ResType::Float(f + f64::from(b)),
}
})
);
}