How can I approximate method overloading?

Function Overloading is Possible!!! (well, sorta...)

This Rust Playground example has more a more detailed example, and shows usage of a struct variant, which may be better for documentation on the parameters.

For more serious flexible overloading where you want to have sets of any number of parameters of any sort of type, you can take advantage of the From<T> trait for conversion of a tuple to enum variants, and have a generic function that converts tuples passed into it to the enum type.

So code like this is possible:

fn main() {
    let f = Foo { };
    f.do_something(3.14);               // One f32.
    f.do_something((1, 2));             // Two i32's...
    f.do_something(("Yay!", 42, 3.14)); // A str, i32, and f64 !!
}

First, define the different sets of parameter combinations as an enum:

// The variants should consist of unambiguous sets of types.
enum FooParam {
    Bar(i32, i32),
    Baz(f32),
    Qux(&'static str, i32, f64),
}

Now, the conversion code; a macro can be written to do the tedious From<T> implementations, but here's what it could produce:

impl From<(i32, i32)> for FooParam {
    fn from(p: (i32, i32)) -> Self {
        FooParam::Bar(p.0, p.1)
    }
}
impl From<f32> for FooParam {
    fn from(p: f32) -> Self {
        FooParam::Baz(p)
    }
}
impl From<(&'static str, i32, f64)> for FooParam {
    fn from(p: (&'static str, i32, f64)) -> Self {
        FooParam::Qux(p.0, p.1, p.2)
    }
}

And then finally, implement the struct with generic method:

struct Foo {}

impl Foo {
    fn do_something<T: Into<FooParam>>(&self, t: T) {
        use FooParam::*;
        let fp = t.into();
        match fp {
            Bar(a, b)    => print!("Bar: {:?}, {:?}\n", a, b),
            Baz(a)       => print!("Baz: {:?}\n", a),
            Qux(a, b, c) => {
                print!("Qux: {:?}, {:?}, {:?}\n", a, b, c)
            }
        }
    }
}

Note: The trait bound on T needs to be specified.

Also, the variants need to be composed of combinations of types that the compiler wouldn't find ambiguous - which is an expectation for overloaded methods in other languages as well (Java/C++).

This approach has possibilities... it would be awesome if there's a decorator available - or one were written that did the From<T> implementations automatically when applied to an enum. Something like this:

// THIS DOESN'T EXIST - so don't expect the following to work.
// This is just an example of a macro that could be written to
// help in using the above approach to function overloading.

#[derive(ParameterOverloads)]
enum FooParam {
    Bar(i32, i32),
    Baz(f32),
    Qux(&'static str, i32, f64),
}

// If this were written, it could eliminate the tedious
// implementations of From<...>.

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);
}