What do you think of function overload?

Hello. I found an idea of overload in Rust here.
And I want to use it because I come from Elixir.
Do you love this idea, rustaceans?

For people having as hard a time finding the code on the linked page as I did, here’s a copy:

trait DoSomething<T> {
    fn do_something(&mut self, c:T);
}

struct DoSomethingImpl {
}

impl DoSomething<i32> for DoSomethingImpl {
    fn do_something(&mut self, c:i32) {
        println!("i32");    
    }
}
impl DoSomething<f32> for DoSomethingImpl {
    fn do_something(&mut self, c:f32) {
        println!("f32");
    }
}

fn main() {
    let mut d = DoSomethingImpl{};
    DoSomething::do_something(&mut d, 10i32);
    DoSomething::do_something(&mut d, 1.5f32);
}

Rust Playground

output

i32
f32

I think that's an accepted pattern. Structs implementing the From trait very often have multiple Foo::from.

1 Like

As to answer the question: Yes, traits allow overloading in Rust, as long as the function arity (i.e. number of arguments) stays the same.

In fact, the standard library uses a similar approach for slice indexing, e.g. in the .get(…) method and related API; similarly for str, too.

The difference is that there is an additional layer of wrapping method, e.g. as if you did something like

trait DoSomething<T> {
    fn do_something_trait_method(&mut self, c:T);
}

struct DoSomethingImpl {}
impl DoSomethingImpl {
    fn do_something<T>(&mut self, c: T) where Self: DoSomething<T> {
        self.do_something_trait_method(c)
    }
}

impl DoSomething<i32> for DoSomethingImpl {
    fn do_something_trait_method(&mut self, c:i32) {
        println!("i32");    
    }
}
impl DoSomething<f32> for DoSomethingImpl {
    fn do_something_trait_method(&mut self, c:f32) {
        println!("f32");
    }
}

fn main() {
    let mut d = DoSomethingImpl{};
    DoSomethingImpl::do_something(&mut d, 10i32);
    DoSomethingImpl::do_something(&mut d, 1.5f32);
}

and the second different in the standard library is that the order of arguments to the trait is reversed, I: SliceIndex<Self>, but those are fairly arbitrary choices.

The wrapper method has the advantage that method syntax will work without the need to import any traits.

1 Like

I think the current consensus is that classic type-based function overloading is a poor fit for Rust, because it would interact poorly with type inference. Rust's existing collect() that behaves like an overloaded method is already annoying and needs type hints.

Overloading based only on the number of function arguments could theoretically work, but the convention of writing extra methods like new_with_foo() or builder patterns works okay enough that overloading hasn't been a prioroty.

7 Likes

The biggest reason that I'm pretty sure Rust won't ever get "normal" overloading is that having both the existing trait resolution rules and additional overload resolution rules is too complicated.

Something like C# can get away with complicated overload resolution rules (which often change, because getting them to match intuition is hard) because there's no "interface resolution rules" -- they're all right there on the type, and its type system is almost entirely forward-only. (The places where it's not, like the new new() support, then mix poorly with overloads because running overload resolution with unknown types is particularly hard.)

But since Rust already has similar-in-spirit rules for finding a trait match, it shouldn't also grow similarly-complicated rules for overload matching.

And when Rust (eventually™) gets variadic generics, then the same "function that delegates to a trait" pattern will work for arity overloading too.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.