How can I approximate method overloading?

I am modeling an API where method overloading would be a good fit. My naïve attempt failed:

// fn attempt_1(_x: i32) {}
// fn attempt_1(_x: f32) {}
// Error: duplicate definition of value `attempt_1`

I then added an enum and worked through to:

enum IntOrFloat {
    Int(i32),
    Float(f32),
}

fn attempt_2(_x: IntOrFloat) {}

fn main() {
    let i: i32 = 1;
    let f: f32 = 3.0;

    // Can't pass the value directly
    // attempt_2(i);
    // attempt_2(f);
    // Error: mismatched types: expected enum `IntOrFloat`

    attempt_2(IntOrFloat::Int(i));
    attempt_2(IntOrFloat::Float(f));
    // Ugly that the caller has to explicitly wrap the parameter
}

Doing some quick searches, I've found some references that talk about overloading, and all of them seem to end in "we aren't going to allow this, but give traits a try". So I tried:

enum IntOrFloat {
    Int(i32),
    Float(f32),
}

trait IntOrFloatTrait {
    fn to_int_or_float(&self) -> IntOrFloat;
}

impl IntOrFloatTrait for i32 {
    fn to_int_or_float(&self) -> IntOrFloat {
        IntOrFloat::Int(*self)
    }
}

impl IntOrFloatTrait for f32 {
    fn to_int_or_float(&self) -> IntOrFloat {
        IntOrFloat::Float(*self)
    }
}

fn attempt_3(_x: &dyn IntOrFloatTrait) {}

fn main() {
    let i: i32 = 1;
    let f: f32 = 3.0;

    attempt_3(&i);
    attempt_3(&f);
    // Better, but the caller still has to explicitly take the reference
}

Is this the closest I can get to method overloading? Is there a cleaner way?


Yes, there is, and you almost got it already. Traits are the way to go, but you don't need trait objects, use generics:

#[derive(Debug)]
enum IntOrFloat {
    Int(i32),
    Float(f32),
}

trait IntOrFloatTrait {
    fn to_int_or_float(&self) -> IntOrFloat;
}

impl IntOrFloatTrait for i32 {
    fn to_int_or_float(&self) -> IntOrFloat {
        IntOrFloat::Int(*self)
    }
}

impl IntOrFloatTrait for f32 {
    fn to_int_or_float(&self) -> IntOrFloat {
        IntOrFloat::Float(*self)
    }
}

fn attempt_4<T: IntOrFloatTrait>(x: T) {
    let v = x.to_int_or_float();
    println!("{:?}", v);
}

fn main() {
    let i: i32 = 1;
    let f: f32 = 3.0;

    attempt_4(i);
    attempt_4(f);
}

See it working here.


Here's another way that drops the enum. It's an iteration on Vladimir's answer.

trait Tr {
  fn go(&self) -> ();
}

impl Tr for i32 {
  fn go(&self) {
    println!("i32")
  }
}

impl Tr for f32 {
  fn go(&self) {
    println!("f32")
  }
}

fn attempt_1<T: Tr>(t: T) {
  t.go()
}

fn main() {
  attempt_1(1 as i32);
  attempt_1(1 as f32);
}