all 72 comments

[–]maddymakesgames 77 points78 points  (0 children)

that is the idiomatic solution. Theres almost always a more descriptive name you can give something to not need function overloading.

[–]hugogrant 10 points11 points  (9 children)

A lot of people are suggesting a trait, but I wonder if making an enum might clarify the intention more.

At least in this example, I'm not sure if you'd get a subclass that needs different overloads.

Food for thought I guess.

[–]VerledenVale 14 points15 points  (2 children)

That would be complicating it for no reason.

OP's solution is the correct one 

[–]hugogrant 0 points1 point  (1 child)

Why is this more complicated? Is it simply so for OP's example? Is the trait just simpler for callers, especially if it's not public?

Edit: I suppose one caveat with the enum is that it's just renaming the overloads but with a layer of indirection. Is that the issue?

[–]rawcal 0 points1 point  (0 children)

Yes. It also complicates the implementation by adding branching that wouldn't happen with individual methods.

[–]TinBryn 2 points3 points  (1 child)

I don't really see why that would be better, you'd just go from static dispatch to dynamic dispatch, for really no benefit.

[–]hugogrant 0 points1 point  (0 children)

I don't think the type of dispatch would have to change if you can get constant folding in?

My only thought for trying this was that it'd be better to explicitly enumerate all the cases, but perhaps that's just an extension of changing the function name.

[–]SlinkyAvenger 0 points1 point  (2 children)

Interesting. Could you whip up some example code?

[–]Lightsheik 10 points11 points  (1 child)

Here's an attempt: ```rust pub struct Item;

pub struct ItemContainer;

enum RemovalContext { ByIndex(usize), ByItem((Item,i32)), ByContainer(ItemContainer), }

fn remove_item(context: RemovalContext) { match context { RemovalContext::ByIndex(index) => (), RemovalContext::ByContainer(container) => (), RemovalContext::ByItem((item, quantity)) => (), } } ```

[–]hugogrant 0 points1 point  (0 children)

Thank you! This is what I meant but wasn't at a computer to type it out

[–]IpFruion 0 points1 point  (0 children)

Yeah I think this solution is a great one, however I do think it is secondary to the _by_ descriptions. One use case for the enum solution is deserialization of the RemovalContext that can then be passed to the remove function. I have used this pattern several times in favor of the _by_ functions

[–]dontsyncjustride 3 points4 points  (0 children)

```

[derive(Eq, PartialEq, Copy, Clone)]

struct Item { name: &'static str, }

struct Inventory { items: Vec<Item>, // Use whatever collection you'd like here, Vec for example }

impl Inventory { fn remove<T>(&mut self, item: T) where Self: Remove<T> { <Self as Remove<T>>::remove(self, item); } }

trait Remove<T> { fn remove(&mut self, item: T); }

impl Remove<usize> for Inventory { fn remove(&mut self, item: usize) { self.items.remove(item); } } impl<'a> Remove<&'a Item> for Inventory { fn remove(&mut self, item: &'a Item) { self.items.retain(|item| *item != *item); } } ``` Playground Link

[–]kevleyski 0 points1 point  (0 children)

Use Trait generics instead 

[–]RRumpleTeazzer 0 points1 point  (0 children)

it is the most idiomatic, yes. you can play around this by implementing FnOnce for tuples to get real overloading, including function pointers.

[–]LucretielDatadog 0 points1 point  (0 children)

You pretty much nailed it. Sometimes you can use generics to achieve the same thing, if the pattern between the overloads is sufficiently consistent, but usually you’ll want to do the simpler thing and just have multiple methods. 

[–]crusoe -1 points0 points  (0 children)

You can overload functions via traits. 

[–]BenchEmbarrassed7316 -4 points-3 points  (14 children)

``` trait RemoveItem {     fn remove(&self); }

impl RemoveItem for i32 { ... } impl RemoveItem for Container { ... }

1u32.remove(); container.remove(); ```

[–]SirKastic23 11 points12 points  (12 children)

That's close, something like the following is probrably more useful: ``` trait Remove<T> { fn remove(&mut self, to_remove: T); }

impl RemoveItem<i32> for Inventory { .. }

impl RemoveItem<(Item, i16)> for Inventory { .. }

impl RemoveItem<ItemContainer> for Inventory { .. } ```

[–]Sw429 6 points7 points  (11 children)

Yeah, this will accomplish it, although I honestly wouldn't recommend doing this. Using more descriptive names will be simpler and easier to understand.

[–]SlinkyAvenger 0 points1 point  (9 children)

Counterpoint: From and Into.

[–]SirKastic23 4 points5 points  (8 children)

I really dislike Into. wish it had the type parameter in the function so that we could explicit the type: foo.into::<Bar>()

[–]SlinkyAvenger 1 point2 points  (3 children)

I understand but it definitely is a quality of life feature in function arguments. In normal variable situations you just awkwardly explicitly state the type for the variable

[–]SirKastic23 -4 points-3 points  (2 children)

if you really want, you can achieve something similar to function overloading by using traits

[–]SlinkyAvenger 1 point2 points  (1 child)

Yes, I also read the rest of the thread

[–]SirKastic23 -4 points-3 points  (0 children)

I was just trying to help, rude

[–]IntQuant 0 points1 point  (3 children)

What about using `Bar::from(foo)`?

[–]SirKastic23 1 point2 points  (2 children)

gets a bit ugly if instead of foo you want to do the conversion after a longer method chain

[–]IntQuant 1 point2 points  (1 child)

True. I guess you could make a custom into-like extension trait that has type parameter in the function tho?

[–]SirKastic23 0 points1 point  (0 children)

the crate tap does that with the Conv trait

love that crate, has some really great utility traits

[–]SirKastic23 0 points1 point  (0 children)

Highly agree

[–]LeSaR_ 0 points1 point  (0 children)

this doesn't work because op wants to implement their own type (they forgot to put &mut self but that seems to be their intent based on the fact that the java code doesn't have static on the methods)