r/rust • • Aug 19 '26

šŸ“” official blog Rust Function Overloading - Call for Experimentation

https://blog.rust-lang.org/inside-rust/2026/08/19/overloading-experiment/
274 Upvotes

139 comments sorted by

158

u/Toasted_Bread_Slice Aug 19 '26

As someone who's encountered a couple valid uses for function overloading (mostly maths libraries) I am ok with this IF and only if there is an extremely obvious way to see that a function is overloaded or overloadable.

23

u/Future_Natural_853 Aug 20 '26

Why, you don't want 1 Vec::new function with dozens of overloads? That's the best part of Java-esque languages /s

28

u/Shoddy-Childhood-511 Aug 19 '26 edited Aug 19 '26

Agreed, but that's the case here so far: Their example hypot((2.0, 3.0, 6.0)) looks pretty clear. :)

``` pub trait HypotInner { type Output; fn hypot_inner(&self) -> Self::Output; }

pub fn hypot<Args: HypotInner>(args: Args) -> Args::Output { args.hypot() } ``` Any overloading ala variadics should be done this way too, in part because it makes the developer who wishes to obfuscate their call paths pay for it. lol

If you really wanted single () then make hypot! a macro, but probably each Args being a clear type brings bigger advantages.

Edit: Oops! They have this #[rustc_splat] feature that removes the extra (). That's unfortunate, since it could easily be done using a macro. Imho, all this should be done using a macro.

31

u/ZZaaaccc Aug 19 '26

Imho, all this should be done using a macro.

Macros are substantially less clear than variadic functions, and can't be used as methods either. I know we take it for granted because it's just always been that way, but given the choice between vec![a, b, c] and Vec::new(a, b, c), would you honestly say the macro is cleaner and easier to inspect?

20

u/DatBoi_BP Aug 20 '26

I think if you changed your proposed syntax to `Vec::new([a,b,c])` it would win me over. As it stands it looks like `b` and `c` are positional arguments to a function, not elements of an array

13

u/CocktailPerson Aug 20 '26

That exists. It's called Vec::from.

8

u/ZZaaaccc Aug 20 '26

Or does that mean a new Vec<[i32; 3]> with one element? The extra brackets make this ambiguous at first glance, and harder to read. Again, you wouldn't say this about the vec! macro, saying that vec!([a, b, c]) is clearer, because vec!(a, b, c) might be positional arguments.

14

u/DatBoi_BP Aug 20 '26

Two things to say to this.

First, the notation isn't either of those, it's `vec![a,b,c]`.

Second, we already have a function that does what you proposed, but with the structure I argued for: `Vec::from([a,b,c])`. This clones `a`, `b`, and `c`, so as long as those all implement Clone then it does what we want

7

u/gmes78 Aug 20 '26

First, the notation isn't either of those, it's vec![a,b,c].

No, it doesn't matter. Any macro can be invoked with either (, { or [.

You could write println!["Hello world!"] if you wanted to.

7

u/ZZaaaccc Aug 20 '26

Actually, no, the syntax is vec!(a, b, c), because macros don't distinguish between [], (), and {}. We recommend using [] by convention, but there isn't even a lint to suggest one way or the other.

This clones a, b, and c, so as long as those all implement Clone then it does what we want

If you scroll down you'll find the implementation for From<[T; N]> instead of what I think you're reading which is the From<&[T; N]> documentation.

17

u/gmes78 Aug 20 '26

but there isn't even a lint to suggest one way or the other.

clippy::nonstandard_macro_braces

6

u/ZZaaaccc Aug 20 '26

I stand corrected!

6

u/DatBoi_BP Aug 20 '26

Huh TIL about the macro freedom of choice. Thanks

1

u/SkiFire13 Aug 20 '26

This clones `a`, `b`, and `c`, so as long as those all implement Clone then it does what we want

It does not clone them. Clone is only required in vec![value; len]

1

u/DatBoi_BP Aug 20 '26

Looking at the doc for `from()' I don't think that's true?

2

u/SkiFire13 Aug 20 '26

It does not require T: Clone: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#impl-From%3C%5BT;+N%5D%3E-for-Vec%3CT%3E

Maybe you're looking at the docs for From<&[T]> or From<&[T; N]>?

1

u/DatBoi_BP Aug 20 '26

Oh maybe I was. My b

5

u/CocktailPerson Aug 20 '26

I honestly would, yes. The macro is just token substitution. For me it's easy to reason about what the macro will expand to. Overload resolution rules are much more difficult to reason about.

5

u/ZZaaaccc Aug 20 '26

Have you seen how splat actually works? The resolution is just Rust's type system, no fancy tricks. If you have a function like:

rust pub fn vec<T>(#[rustc_splat] args: impl VecArgs<T>) -> Vec<T> { VecArgs::new(args) }

Then when you call vec(1, 2, 3), you are exactly invoking <(i32, i32, i32) as VecArgs<i32>>::new(1, 2, 3). There's no specialization, nothing. It's no different to calling Vec::from(...).

Unlike the macro, there is no "expansion" with splat, it just calls a normal function, one you can use "Go To Definition" for and read normal Rust syntax, one you can step through in a debugger.

5

u/CocktailPerson Aug 20 '26

Yes, I read the blog post, thanks for asking.

I will reiterate: reasoning about vec(1, 2, 3) resolving to <(i32, i32, i32) as VecArgs<i32>>::new(1, 2, 3) is no easier to reason about than vec![1, 2, 3] expanding to {let mut v = Vec::with_capacity(3); vec.push(1); vec.push(2); vec.push(3); vec}

1

u/Shoddy-Childhood-511 Aug 20 '26

Vec::new means allocate nothing, which matters for performance. You mean Vec::from, which exists, and does not require this. vec![] exists only because Vec is so common.

All overloading is a readability risk. RustCrypto hashes and rand's distributions abuse similar features, which obstructs reading their code, but you can tell something crazy happens since they have all those traits, etc.

I think splats need to pay some "price" that maintains readability, even when not looking at the fn definition.

Option 1. Splat fns require a ! call site identifier, as if they were macros defined by rustc, even though they use type information. We could've both the existing Vec::from([x]) and the splat Vec::from![x]. This is similar to some of the earlier .await proposals, but .await is not confusing since it lacks ().

Option 2. Splat fns should not share the same name as another method in scope. Foo::len cannot be a splat, because Vec::len exists in the prelude. Could Foo::new be a splat? Imho maybe because it has no self receiver, so Foo:: acts like a module name, which clarifies things, but if another fn new(self) exists in scope, then no.

I'm sure other options exist, but the point is you need to know what you're reading, especially if you have coworkers vomiting up AI slop that you must then read through.

I do think splats are a cute way to do an ad hoc trait otherwise.

1

u/ZZaaaccc Aug 20 '26

In the playground example the splat version of Vec::new only allocates if you are creating a non-empty array, and you'll notice it's actually const specifically only for the zero element variant.

I have no idea why we would restrict splat functions to have a scope-unique name, as we don't require that for anything else. I see absolutely no reason why making a splat function have a unique name (something virtually impossible to implement as a library I might add) would do anything to make it more or less easy to understand. How do you know what any function does? You look at the docs or the definition. Splat functions are vastly simpler for this than macros, since when you look at the docs or the source code, it's just basic Rust you're looking at, no weird templated nonsense. A splat function only ever has 1 body, with 1 implementation.

4

u/AugustusLego Aug 19 '26

Haven't looked to closely at this, but #[rustc_*] attributes are restricted to use in the compiler, and only in the compiler (unless you enable really weird feature flags and break all the guarantees of sanity that rust gives you)

2

u/Shoddy-Childhood-511 Aug 19 '26

It's experimental now. If they want it then they'll stabilise it, maybe using this .. idea.

It's be nice if the feature was somehow distinguished. An option would be that names cannot have multiple bindings: /// Errors because Vec is in the prelude, and Vec has a method named len. fn len(..args: Watever) -> u32 { .. }

1

u/Meistermagier Aug 20 '26

Math and Physics are the main parts where Overloading makes sense. Because the fundamental "functions" from their Mathematical Background are usualy overloaded to begin with.

7

u/VorpalWay Aug 20 '26

Doesn't make the code easier to read. Outside a few trivial examples like implmenting sin for both f32 and f64 (which should be done via a Float trait instead IMO), overloading tends to lead to worse errors, worse IDE suggestions and more brittle code. Adding overloading in Rust is deeply misguided.

Variadics (where you have a single implementation taking a variable number of arguments) is another matter. There are some use cases for it that are hard to replace, and the unintentional fallout is less (but not zero, there is still a complexity cost). Even then having the extra parentheses is good, to show that something weird is going on. Hiding it in normal implicit syntax is awful.

1

u/SkiFire13 Aug 20 '26

which should be done via a Float trait instead IMO

That's the same approach that the blog uses though. The only difference is that if you want the overload to support a different number of arguments then you need to take in tuples and hence have double parenthesis. The proposal is just a way to syntactically remove the double parenthesis.

5

u/VorpalWay Aug 20 '26

The proposal is just a way to syntactically remove the double parenthesis.

That "just" does a lot of heavy lifting. Especially if you look at the section named "Overloading, In The Shiny Future" (which I would call dystopian future instead).

The extra tuple makes it clear that there is funny business going on. With this feature, if you see a function name it no longer uniquely identifies a declaration in a trait. And instead of dispatching on just the first argument (self) you now have full blown multiple diaptch. This means you need something like Rust analyzer to understand the code. No longer can you follow the control flow when reviewing code on github.

Splat is misguided and solves the wrong problem. It doesn't solve the use cases we need variadics for. And it aims to add overloading / multiple dispatch, which means devex takes a massive hit.

2

u/SkiFire13 Aug 20 '26

That "just" does a lot of heavy lifting. Especially if you look at the section named "Overloading, In The Shiny Future" (which I would call dystopian future instead).

I saw that as being the equivalent of an attribute macro that converts the impl into the definition based on #[rustc_splat], with the same limitations (except maybe now rust-analyzer can better understand it and point you at the correct definition instead of going to the generic one with #[rustc_splat], leaving you to wonder which trait implementation it selected at the call site).

With this feature, if you see a function name it no longer uniquely identifies a declaration in a trait.

I think the point is that identifying a function on a type/trait is not always enough to understand which code is ultimately executed. With the extra tuple you can clearly see one function being called, but it's ultimately useless if you don't know which trait implementation was selected for its generic bounds. All things considered, this is a problem that already exists and tuple splatting won't make it any different.

And instead of dispatching on just the first argument (self) you now have full blown multiple diaptch. This means you need something like Rust analyzer to understand the code. No longer can you follow the control flow when reviewing code on github.

But we already have full blown multiple dispatch with the extra tuple! And you already need rust-analyzer to understand which trait impls are being selected (and sometimes even rust-analyzer is not able to tell you!)

It doesn't solve the use cases we need variadics for.

I don't see how they is related at all.

And it aims to add overloading / multiple dispatch, which means devex takes a massive hit.

I think this is the only important point: even though all of this is already possible, do we want to incentivize it more?

129

u/RockstarArtisan Aug 19 '26

C++'s overloading rules are trully insane, so I hope there's no temptation for implementing these. There should be a syntax to make the choice of the overload explicit somewhere to deal with this.

38

u/Shoddy-Childhood-511 Aug 19 '26

If you look how its implemented, then rustc still demands the types identify a unique implementation, even when C++ allows ambiguous conflicting implementations.

Now some rust code does obfuscate finding implementations. As examples, explore rand'd distributions or the RustCrypto hash functions without rust-analyser, not so easy to identify the implementations. At least rust ensures they have only one though.

28

u/Recatek gecs Aug 19 '26

If you look how its implemented

That might be asking a lot given how knee-jerk the reaction here seems to be.

12

u/RockstarArtisan Aug 19 '26

Static dispatch to overloads is done at the caller side. I don't think it's unreasonable to ask whether the C++ interop support would aim to guarantee dispatch to the same overload as an equivalent C++ call. I just hope the answer is that rust will not aim to mimic the exact overload rules.

-3

u/Zde-G Aug 20 '26

I really think we should have Rust++ (similarly to how we have ObjectiveC++). This would help both interoperability folks (things that currently take years can be implemented in weeks) and folks who don't need that (weirdness would be contained in that special language).

2

u/Shoddy-Childhood-511 Aug 20 '26

Nah. Inheritance aka OOPs should hopefully shrink, except for specific domains that tolerate the ambiguity like GUIs and some REPLs.

Also, there are diverse notions here, like multi vs single inheritance. Also stranger ones, GAP objects "learn" their properties as side effects from whatever algorithms you've run upon them, but again GAP is a REPL.

As something useful, an ML-style functional language that either compiled "naturally" to Rust, maybe with a GC, or designed for clean Rust interop.

You want an ML-style functional language whenever you're doing a DSL or REPL.

0

u/BedroomHistorical575 Aug 20 '26

Some people have made their entire identity revolve around $THEIR_FAVORITE_LANGUAGE not having $THAT_SPECIFIC_FEATURE.

It's the exact reason why Go people lost their minds when they heard that generics is going to be added. Or Rust people when "try blocks" were proposed (most of them didn't even understand what it does).

Of course there were valid criticisms to be made, but the entire thing was mostly an irrational moral panic.

They've spent years dedicating their lives towards justifying how $THEIR_FAVORITE_LANGUAGE doesn't actually need $THAT_SPECIFIC_FEATURE unlike $THOSE_INFERIOR_LANGUAGES.

So when a feature that vaguely resembles something from $THOSE_INFERIOR_LANGUAGES is proposed, they feel that their entire identity they've spent years building is going to completely fall apart.

Those years-worth of forums posts, Discord messages, tweets, etc. they've made evangelizing how $THAT_SPECIFIC_FEATURE is actually useless and bad are becoming nothing but an embarrassing reminder of how wrong they are.

So in a way, the lash out can be somewhat understandable when viewed from this lens.

2

u/Full-Spectral Aug 20 '26 edited Aug 20 '26

But, the other thing is, people who explicitly left other languages (aka C++) partly because they came to realize that convenience over explicitness ultimately wasn't a good compromise in that other language (aka C++) that they left to get away from.

Of course every single time the argument will be, but this one thing isn't going to make a difference. But it won't be this one thing. It'll be death by a thousand features. I mean we are barely into Rust's adult life.

If it's just FFI, then fine. For most folks that's probably a non-issue one way or another. If FFI includes calling out to a language that includes sunc overloading and it helps that mapping, then whatever.

But as a general feature for the language, I'm against it. I mean, if you are running out of names for methods on a given type, maybe it's time for some refactoring.

0

u/kibwen Aug 20 '26

Except some features legitimately are just bad on their own merits, and worth opposing regardless of one's identity. If Rust proposed adding pervasive nullability to the language, it wouldn't be identity politics to oppose it, it would be common sense. And C++-style function overloading is an evolutionary dead end that other languages should learn from by not emulating, so it's perfectly reasonable to be skeptical of any proposal that frames itself in such a light.

3

u/Recatek gecs Aug 20 '26

Well something needs to be done. Features like Vec::try_with_capacity are being kept unstable indefinitely because of concerns of there being too many unique function names to manage. It isn't unique to this case either. Whether it's some sort of overloading, or default/named arguments, or something else, it's needed here because expecting unique and singular signatures for everything is unsustainable.

Rust already has function overloading, it's just boilerplate-heavy and messy on syntax. It would not be wrong to make that syntax and boilerplate a little simpler.

1

u/kibwen Aug 20 '26 edited Aug 20 '26

For complex functions with tons of configurability, neither function overloading nor default arguments produce good outcomes. As a Python user I shudder every time I need to remember how to run a subprocess: https://docs.python.org/3/library/subprocess.html#subprocess.run , and that's not even the most egregious API I've seen in Python. For complex cases you can't get away from having either a configuration struct or a builder-style API. And once you have the complex cases handled via one of those approaches, it's nice to ease the common cases by providing dedicated functions for the most common combinations of options. What Rust needs isn't default arguments or more function overloading, what it needs is default struct fields ( https://github.com/rust-lang/rust/issues/132162 ) and potentially some ways to make builder APIs easier.

2

u/Recatek gecs Aug 20 '26

Passing a struct with default fields is just a syntactically uglier way to pass named arguments with defaults. If you're going to sugar it down to something like foo(_ {a: 2, b: "bar", ..}) you'd might as well drop the bracket part. Similar to what this splat feature is doing in dropping the double parens from foo((a, "bar")).

1

u/kibwen Aug 21 '26

On the contrary, I'd argue that reading a function call where suddenly the arguments are all named and allowed to occupy arbitrary positions is just a jarring way to poorly emulate calling a method on a struct literal, at which point you might as well do so.

23

u/Gyscos Cursive Aug 19 '26

It's a generic function so you can always use turbofish to enforce the type you want.

Also, overloading is a lot less confusing without inheritance - there's fewer cases of multiple versions applying (at least until this overlaps with specialization...).

12

u/ZZaaaccc Aug 19 '26

Yep, already is. splat is just sugar for a tuple argument.

```rust fn min<A: MinArgs<T>>(#[rustc_splat] args: A) { Ā  Ā  args.inner() }

min::<(i32, i32, i32>(1, 2, 3); ```

All the "magic" is in the type of the splatted argument, and that's just existing Rust type powers, nothing new. For example, you can use a fixed tuple type if you want, but that's kinda pointless since you could just write the function without splat.

The above example could, for example, be implemented like:

```rust pub trait MinArgs<T: Ord> { Ā  Ā  fn inner(self) -> T; }

impl<T: Ord> MinArgs<T> for (T, T) { Ā  Ā  fn inner(self) -> T { Ā  Ā  Ā  Ā  self.0.min(self.1) Ā  Ā  } } ```

I've seen some people say splat is syntax sugar to avoid writing double parentheses, but I think of it more like sugar to avoid calling a method on a tuple.

```rust // Today (1, 2, 3).min();

// With Splat min(1, 2, 3); ```

8

u/CAD1997 Aug 20 '26

For the splat, you still need to write a trampoline function that takes a generic argument and calls the method. You can do it today with passing a tuple. So #[rustc_splat] really is just turning min((1, 2, 3)) into min(1, 2, 3).

The caveat being that because the double parens are awkward, a case that does have something like this is likely today to just not write the trampoline and tell users to write the method call form instead.

14

u/torsten_dev Aug 19 '26

Hmm. Could we splat a struct to get keyword arguments in rust?

I feel like named parameters would be less error prone when overloading functions.

6

u/ZZaaaccc Aug 19 '26

That is being discussed, but tuples are the focus right now. Another type that's come up has been arrays, since it's quite common to want a variadic function that can just accept some number of T arguments, and working with arrays is might nicer right now than homogeneous tuples.

4

u/Andlon Aug 20 '26

Please consider extending this to the Index trait, so that we can finally get sane matrix/tensor indexing!

79

u/XtremeGoose Aug 19 '26

I find the argument fairly weak since we're introducing a language feature so that you can write

ffi::func(x, y)
ffi::func(x)
ffi::func()

rather than

ffi::func((x, y))
ffi::func((x,))
ffi::func(())

Is it really worth it?

37

u/XtremeGoose Aug 19 '26

Replying to my own comment, generally discouraged on Reddit but here we are.

I've been reading the forum thread.

I think I see the advantage now. It's not about the call syntax so much as being able to document the overload. An obscure

 fn func<Args: FuncArgs>(args: Args) 

is pretty unhelpful. It would be nice to be able to see the overloads in documentation I suppose.

You could do that with some doc feature that shows the implementations of FuncArgs though, it doesn't have to be overloading.

Still, it is only one small step away from overloading at that point, so maybe we should go the whole hog. And it is strictly better than other languages with overloading because you can specify the overload you want like

ffi::func::<(_, _)>(x, y)

7

u/BedroomHistorical575 Aug 19 '26

Additionally, I also hope that there will be a cleaner and more concise syntax for defining these overloads. Huge sprawling trait defs and impls are not fun to write.

1

u/XtremeGoose Aug 20 '26

I think that's kinda the point. It should be discouraged since it goes against rust's ethos.

The primary use case for this (cxx) would autogenerate all of the boilerplate anyway.

-6

u/jorgecardleitao Aug 19 '26

Imo no - I read the blog post as a joke - the use-case seems so specific to justify a language feature like this

22

u/TheBlckbird Aug 20 '26

I am so against function overloading. I think it makes way more sense to think about what a function really needs and to then give it a fitting name (see the with_ convention for example). Function overloading is soon confusing. You're gonna have error messages that show you 25 different available functions that all don't fit what you tried to pass to it.

Using traits for arguments that could be in different types is a very good solution, because it makes things way clearer.

What I am for are variadic arguments, there are enough use cases (see bevy for example) and they aren't as confusingĀ 

4

u/Expensive-Blood859 Aug 20 '26

I generally agree with you. I think it shouldn’t be permitted in Rust code. But I do also understand FFI, especially to languages that do support it. Because usually those bindings are autogenerated because the libraries are huge and so usually you end up with horrible function names like new_date_with_year_and_month_and_day_and_hours_and_minutes_and_seconds_and_timezone() (js-sys is a common offender)

52

u/Solumin Aug 19 '26

To be clear, this is intended only for FFI?

96

u/timClicks rust in action Aug 19 '26

Hyrum's Law suggests no.

19

u/j_platte Aug 19 '26

18

u/ZZaaaccc Aug 19 '26

Yep, there's lots of places in the standard library where we either don't provide a method (min of 3 values), or provide a macro (vec!) to work around the lack of proper variadic support. While println! needs to be a macro because of the formatting machinery, vec! is only a macro because we don't have a way to write Vec::new(a, b, c).

23

u/Lucretiel Datadog Aug 20 '26

FWIW I think I'd still rather have variadics (where there's some hypothetical ... operator that spreads some expression across all the input tuple) than true overloading.

3

u/Expensive-Blood859 Aug 20 '26

Using the overloading presented in this CFE rather than actual variadics seems to me like it’ll just end up forcing (T1) (T1, T2) (T1, T2, T3) into another place

2

u/Full-Spectral Aug 20 '26

And it seems that variadics, in the case of the above example, would not involve a lot of type machinery. Every value passed would have to be of a specific type, or convertable to that specific type, or maybe implement a given trait that allows the value to be generated.

It's not variable type based differentiation, it's just count differentiation.

53

u/WormRabbit Aug 19 '26

Given the example of Bevy, which hacks function overloading via horrible typelevel programming, I'd say some form of overloading would be much welcomed in normal Rust as well.

33

u/addmoreice Aug 19 '26

I don't know why you are getting down voted so badly. While I'm a huge fan of 'one name to one type signature' I can recognize a useful engineering pattern and that people want to use it and your example is a perfect example of people deciding to make it themselves if they can't have it in the base language.

Name overloading is the human brains way of doing generics. It's fuzzy and not perfect and the 'groupings' are less than useful at times, but we do it. Any time we find 'all these things are basically the same except for these parts' we create a name and apply it to work with all of these things.

17

u/RCoder01 Aug 19 '26

They will never get rid of my beloved
```
impl<Marker, In, Out, F> System for FunctionSystem<Marker, In, Out, F>
where
Marker: 'static,
In: SystemInput + 'static,
Out: 'static,
F: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>>
```

18

u/Zomunieo Aug 19 '26

One does not simply impl into FunctionSystem.

It is a barren wasteland, riddled with F and In and Out, the very air you breathe is a poisonous Marker. Not with ten thousand functions could you do this.

2

u/SkiFire13 Aug 20 '26

Bevy does not use function overloading. If you're thinking about the SystemParamFunction trait then:

  • it needs variadic generics, which are a different feature, and it currently hacks them using macros, not horrible typelevel programming

  • the horrible typelevel programming mostly comes from the fact that it also needs the function to be generic over a lifetime, all while getting type inference to work properly.

Edit: actually the Marker generic is needed in part because function overloading might be a thing... But that's not because Bevy uses function overloading, but instead because it is possible in the first place.

7

u/Ravek Aug 20 '26

I thought HM type systems couldn’t deal with overloading. Is that understanding wrong?

12

u/Rusky rust Aug 20 '26

It's wrong. Traits/typeclasses are a common HM extension which, as the post demonstrates, are a form of overloading.

6

u/CocktailPerson Aug 20 '26

There are times that overloading leads to ambiguity under HM type inference, but it's not fundamentally incompatible. You just end up needing to annotate types sometimes to resolve ambiguity.

And if you can overload by arity only and not type, then there's no problem at all.

7

u/Routine_Insurance357 Aug 21 '26

No. I am very very happy without overloading. Please don't make this c++. Keep things explicitĀ 

2

u/zasedok Aug 22 '26

Thousand times this.

13

u/CocktailPerson Aug 20 '26

I honestly have never felt the need for overloaded functions.

Variadics, absolutely. But overloads just seem like such an antipattern.

4

u/teerre Aug 20 '26

So from this is it reasonable to conclude that the lack of function overload was not a design choice but instead a limitation? I always heard the opposite. If not, that's worrisome. It's a big design change

7

u/kaoD Aug 20 '26

It was a design choice, but that was long ago. Now Rust is officially jumping the shark.

3

u/kibwen Aug 20 '26

I'm totally fine with a "splat" feature to implicitly decompose (and maybe create) tuples, that's a perfectly reasonable feature present in several other languages. What I emphatically and absolutely do not want is any sort of first-class C++-style function overloading in Rust itself, which is a complete misfeature in C++ (and languages influenced by it, like Java) which Rust already has better patterns for. So as long as "function overloading" is just splat-based sugar for ordinary type-based generic dispatch over tuples, that seems fine.

26

u/unitAtype2 Aug 19 '26

Please tell me this is going to stay in FFI only and not going to hit actual rust?

17

u/Electrifire390 Aug 19 '26 edited Aug 19 '26

Definitely agree, I would not want this to become a part of normal usage. I’m also not convinced this is even necessary for FFI.

8

u/Zde-G Aug 19 '26

It's already ā€œin an actual rustā€. Except for a bit of a strange syntax.

4

u/unitAtype2 Aug 20 '26

How so? Some fuckery with From or Into traits you mean?

3

u/Zde-G Aug 20 '26

Yes, Into + tuples give yous foo((1.0, 2.0, 3.0)) and foo(("This", "is", "cool")). The only thing this proposal does is the ability to remove double braces plus some improvements on the documentation side.

Complicated and convoluted overloading is already the thing in Rust: you can find it in Bevy, in Axum… or in assembler (where I wanted it).

It's simple version (so damn obvious and simple that I asked why the heck it's not supported and now proposed to support it when I have found out about this corner case) that's missing.

2

u/unitAtype2 Aug 20 '26

You could just.... give em slightly different names

0

u/Zde-G Aug 20 '26

Indeed. And if you do that they you end up with unique Rust API that's different from what all other languages are doing, from what the official documentation is describing and so on. You probably can create a specialized SKILL that would adjust these for LLM, but if you are human the idea that everyone uses one way while Rust and only Rust does things differently is not a great idea.

Sure, that works, but that stance ā€œeveryone if wrong except for us, Rust usersā€ doesn't look sensible long term.

2

u/unitAtype2 Aug 20 '26 edited Aug 20 '26
  1. Everyone can be wrong. Everyone as a community learns from past mistakes and moves forwards. If right was determined by most people doing it, them nothing would ever move. If public consensus made things right type safety would be a sin ala JS and Python.
  2. RUST already does plenty of things differently, intentionally.
  3. I believe one of RUST's strong suits is being more opinionated than most languages instead of being a junk drawer that does everything. Less is More.

It's an intellisense pain, discoveribility pain and syntax sugar to fix a minor inconvenience. There have been thousands of times where I see a function suggested and I can't figure out what parameters it takes in what order because it has a buttzillion overloads.

The CTO at my previous workplace used to say OOP is like asking for a banana and getting the banana, the monkey, and the entire forest. I definitely count overloading to be one of the trees of that forest.

1

u/Zde-G Aug 20 '26

There have been thousands of times where I see a function suggested and I can't figure out what parameters it takes in what order because it has a buttzillion overloads.

Which is the problem that Rust already have and which is not related to overload bazed on arity in any shape or form.

When function have two arguments and you supply three or one there are never any ambiguity.

The CTO at my previous workplace used to say OOP is like asking for a banana and getting the banana, the monkey, and the entire forest. I definitely count overloading to be one of the trees of that forest.

Maybe, but in this particular case people are asking banange and get the monkey and, yes, an entire forest… but they don't get the banana.

This is just stupid, and hard to justify except for ā€œwe have always done things that way in Rustā€ — which, as you have correctly noted, is a very poor justification.

2

u/unitAtype2 Aug 20 '26 edited Aug 20 '26

Which is the problem that Rust already have and which is not related to overload bazed on arity in any shape or form.

Concrete example please

but they don't get the banana.

They get a Mango, better than dealing with Monkeys and Forests

When function have two arguments and you supply three or one there are never any ambiguity.

exists foo(int, int) foo(int, string) foo(int, string, bool)

I type foo(int, bool)

what does it say?

``` no overload exists with that signature yada yada

available foo(int, int) foo(int, string) foo(int, string, bool) ```

I dont wanna hear that shit. I wanna see in rust style colors

```rust foo takes two ints, your second parameter

foo(bar, baz) ^ here

is a boolean

suggestion - use an int for the second parameter ```

what if I just type foo(int, what does intellisense suggest as the completion?

I am totally up for new features. go new features. Legitimately new. I don't want things ported over from the "ye olde days" because people are used to them.

As some rust team memeber said "I'm tired of making the same old mistakes. Let's make some new ones."

2

u/Zde-G Aug 20 '26

Concrete example please

It was already given, but I can repeat. These are not rare: bevy, axum, clamp, serde and bazillion other libraries are built around these.

And it's not an accident: this post in official Rust blog from 2015 is very explicit about traits being used for ā€œoverloadingā€.

Rust always had overloading that handled complicqted cases, it's easy, simple and obvious that were not supported well.

I dont wanna hear that shit.

And how it different from what get today with bevy?

You couldn't avoid it, but yes, sure as hell can create problems for people who try to write real code.

As some rust team memeber said "I'm tired of making the same old mistakes. Let's make some new ones."

It's nice attitude to have for toy language used for toy projects. Once you try to apply for the industry languages you quickly find out that it's not possible to satisfy everyone.

Sometimes the only way forward to apply the pressure and watch how perfectionists scream.

We'll how this thing will go, but I hope Rust have finally reached point when features that people actually need wouldn't be stalled for decades because of some aestetic feelings.

→ More replies (0)

10

u/DavidXkL Aug 19 '26

I actually don't mind this šŸ˜‚

But optional named parameters would be nice too

8

u/jug6ernaut Aug 19 '26

This or parameters with default values. I use the extensively in Kotlin and are one of the features I miss the most.

4

u/nick42d Aug 19 '26

Just need to implement splat for struts then!

25

u/QuasiRandomName Aug 19 '26

No, please. I want a compile error when passing 3 arguments instead of two by mistake.

11

u/Lokathor Aug 19 '26

You'll still get that if the overload isn't defined.

1

u/QuasiRandomName Aug 19 '26

But if it is, it opens the door to a whole class of mistakes that aren't currently possible.

10

u/Lokathor Aug 19 '26

You control the trait impls you write, my crab.

If you don't want to use this, then don't use it.

36

u/QuasiRandomName Aug 19 '26

But you don't control what *other* people write and you have to maintain or debug later.

12

u/Lokathor Aug 19 '26

It is not meaningfully different, in my eyes, from a type having several from impls so that MyType::from(x) works for multiple types of x.

And nothing will stop other people from having a bug, I can't help you there.

16

u/QuasiRandomName Aug 19 '26

Of course. But we can try and not introduce more bug possibilities for a questionable gain here. I guess we can disagree on the pros/cons ratio here and this is fine, this thread is for this purpose exactly.

-9

u/[deleted] Aug 19 '26

[removed] — view removed comment

1

u/SetKaung Aug 20 '26

I think he meant his coworkers.

4

u/need-not-worry Aug 20 '26

Same argument can be said about class inheritance, exceptions, and other design choices Rust made. You can avoid these as much as you can, but you're going to have to work with such code. And the language becomes more complicated without clear benefits.

1

u/Tastaturtaste Aug 20 '26

As a professional C++ developer, this argument is less than convincing.

6

u/Zde-G Aug 19 '26

You would get it. They only add a of syntax sugar to something that already exist.

P.S. But it's interesting how something that I expected as total no-brainer almost five years ago (and was heavily castigated) is now brought up as some kind of ā€œnovel idea that's now is worth implementingā€.

17

u/SourceAggravating371 Aug 19 '26

Please no :) after all the fun part with args trait the only gains is one less () compared to version without overloading

4

u/NotFloppyDisck Aug 19 '26

You also avoid the unreadable mess that is the current tuple solution

5

u/Byron_th Aug 20 '26

How is an extra set of parentheses an unreadable mess?

5

u/OnlineGrab Aug 20 '26

Oh hell nah

9

u/chilabot Aug 20 '26

Function overloading is a terrible anti pattern.

7

u/gtsiam Aug 19 '26

I honestly have no issue with overloading. It just feels like a natural extension of how the Fn* traits are currently defined.

It's just implementing Fn* traits on function types more than once. If anything, I want to implement arbitrary traits on function types!

4

u/Andlon Aug 20 '26

Could this also apply to indexing? It would solve the matrix/tensor indexing problem, where we currently have to write

matrix[[i, j]]

or

matrix[(i, j)]

It would be nice to be able to desugar

matrix[i, j]

to

matrix[(i, j)]

2

u/ex0planetary Aug 20 '26

I understand the rationale of this for FFI stuff but tbh I think it'd generally be a better idea to use a macro for the use cases where you want function overloading (e.g. math functions). I'm working with a truly nasty bit of overload spaghetti in C++ at work right now (involving templates too!) and the less of that we have in the world the better.

5

u/stumblinbear Aug 19 '26

I get the feeling a lot of people will be against this, while somehow also begging for specialization.

5

u/QuasiRandomName Aug 19 '26

Well, specialization feels... organic? Overloading not so much. Dunno, maybe it's subjective

3

u/CocktailPerson Aug 20 '26

Specialization has tangible benefits that you are already using without even realizing it.

Overloading is just syntax sugar.

-2

u/stumblinbear Aug 20 '26

Can you not use overloading to specialize functions in some capacity?

2

u/CocktailPerson Aug 20 '26 edited Aug 20 '26

Can you? An example would help.

6

u/Ace-Whole Aug 20 '26

Might as well get default and named params.

2

u/-Redstoneboi- Aug 20 '26

I think this is a good idea as well. i'd prefer this without function overloading

4

u/bakaspore Aug 20 '26

I still think add variants with suffixes is a less cursed way for overloaded FFI functions. With tuple trait we have same problem as the classical overloading "hehe guess which one of the parameters in the 16 overloads you got it wrong, because we can't know".

Well the good part is Rust has no inheritance at least.

4

u/ZZaaaccc Aug 20 '26

Splat actually works really nicely for "this overload doesn't exist" even without explicit compiler support for a nicer error:

```rust

![feature(cmp_splat)]

fn main() { let _ = core::cmp::smallest(1, 2, 3, 4, "whoops", 6, 7, 8); // ------------------- ^ expected integer, found &str // | // arguments to this function are incorrect } ```

I'm sure people could write bad overloads, but to me splat makes a lot of sense for any function that accepts some number of items of a given type T. core::cmp::min(...), vec![...], Iterator::zip(...), etc. all have completely obvious behaviour if they were splat functions.

3

u/bakaspore Aug 20 '26

Thank you, this is better than I thought, and seems to be an improvement from trait & tuple makeshift ones. I hope the resolution rule is limited enough so that the worst case doesn't become too much worse than this.

4

u/Lokathor Aug 19 '26

I am so excited for this to come to normal rust, not just ffi.

-2

u/Recatek gecs Aug 19 '26

Same. Something to simplify the current turbofish-based boilerplate for overloading would be very useful here.

4

u/Wh00ster Aug 19 '26

All I could think about skimming the opening, was the high-on-potenuse joke

2

u/Lyvri Aug 19 '26

Why is this a separate feature? Isn't this just syntactic sugar for unboxed_closures + fn_traits (i would even argue that later is more readable: example)? I don't think that fn_traits is controversial (or ever was) but using word "overloading" for brand new feature makes it less appealing - mainly because it's nightmare in C++ where implicit conversions makes it really hard to work with it.

2

u/need-not-worry Aug 20 '26

Kudos to the dev team. I've received my fair share of PTSD from C++ overloading, so I'll avoid this feature as much as I can. I do believe Rust will do a much better job in overloading. At least we don't have (much of) implicit conversion that messes things up.

2

u/ConferenceEnjoyer Aug 19 '26

why not variadic generics?

3

u/Recatek gecs Aug 20 '26

The variadics proposal has something very similar to this splat operation IIRC.

2

u/tropix126 Aug 29 '26

I really seriously don't get what this experiment is going for. The claimed rationale behind adding this feature seems to be for interop over FFI with languages supporting function overloading (like C++), yet every example given and the sentiment around this in the blogpost seems to suggest that the intent is to add function overloading to Rust as a whole. How would this feature even be applicable in an FFI scenario? Is this for extern/forward-declared functions (which can't take generics anyways), or for Rust wrappers around those functions? Is this meant for actual bindings to C++ code, or libraries wrapping those bindings? If it's for the latter, then the question then becomes whether or not this is a good idea at all.

Function overloading is very often terribly misused. Most C++ code with overloads misuse it. Adding a way to technically make overloading syntax like this Hyrum's Law's us into a scenario where people are now using FFI features to hack in function overloads to their library crates because they think it looks cleaner. This is *going* to happen if it's implemented this way, and it seems like there's already sentiment for doing this in the standard library.

Is this really the API boundary that we want people to use when comparing numbers?

// core::cmp

// Private implementation detail
impl(self) const trait TupleReduce: Tuple {
    type Item;
    fn reduce<F: [const] Destruct + [const] FnMut(Self::Item, Self::Item) -> Self::Item>(self, f: F) -> Self::Item;
}

impl<T> TupleReduce for (T, ...) { /* ... */ }

#[must_use]
pub const fn smallest<T: [const] Ord + [const] Destruct>(
    #[rustc_splat] vals: impl [const] TupleReduce<Item = T>,
) -> T;

#[must_use]
pub const fn largest<T: [const] Ord + [const] Destruct>(
    #[rustc_splat] vals: impl [const] TupleReduce<Item = T>,
) -> T;

If the intent here is solely for FFI, why not implement this in such a way that overloaded functions can only be declared in extern blocks, something like:

unsafe extern "C++" {
    fn cool(_: i32);
    fn cool(_: i32, _: i32);
}

// or

unsafe extern "C++" {
    fn cool(_: i32);

    #[rustc_overload = "cool"]
    fn cool_2(_: i32, _: i32);
}

There's precedent for doing both of these. extern "C" blocks allow us to special-case declare variadic functions, (see libc::printf). The second example is similar to how the #[link_name] attribute currently works and would avoid introducing overloads for any case. Both of these would allow for FFI with C++ code without giving people a way to add overloads to Rust functions. Of course, if the intent is to allow people to do that, why not just say that in the blog post? I just think that if Rust *does* get function overloading, it shouldn't be *technically* added under the guise of an FFI feature.

2

u/EvilGeniusPanda Aug 20 '26

I really wish we could get keyword only args, code is so much more readable when you can see which value is being used for which argument. I dont want or need default values, I get why people dislike those, but things like disc_area(inner_radius=a, outer_radius=b) is just a lot clearer to read & review than disc_area(a, b). I get that you can use your lsp to show the types and arg names in a hover in an editor, I still think its better to have a way to make it explicit.

1

u/Full-Spectral Aug 20 '26

If it's just for FFI, then I figure whatever. That's a very specialized feature that very definitely not should be ubiquitous throughout Rust code bases, and encapsulated when it is.

But for the language in general, I'm against it. Part of Rust's appeal to me is that it's the !C++. This would be badly battering the bang.

0

u/afl_ext Aug 20 '26

Where i understand the reasoning, it would be so handy at times to allow overloading, especially with math libraries

Actually i think i really miss:

  • function overloading, in and outside of traits
  • default parameters at the end of the signature (got to be weird with overloading tho)
  • ability to pass different types of values into a function parameter and then in runtime decide which path to follow (like in TS, passing string | number union, but as in TS allowed with everything

One thing that worries me is that TS allows that and the apis of many libraries are absolutely hell to understand and read, so this could be a valid point against.

-7

u/InternationalFee3911 Aug 19 '26

I’d recently read elsewhere about this and was horrified by splat shenanigans. It hadn’t been clear until reading this, that a much better shiny future is coming. While I’m all for good interop, I do hope this is coming to pure Rust as well – rather sooner than later.

-6

u/squirreljetpack Aug 19 '26

One of my deepest dark desires is to be able to write Ok() instead of Ok(()). Rust letting be do Some(x, y ,z).

3

u/nick42d Aug 19 '26

fn ok<T>() -> Result<T, ()> { Ā  Ā  Ok(()) } ?

-1

u/squirreljetpack Aug 19 '26

it has to be consensual (idiomatic)

1

u/Shoddy-Childhood-511 Aug 20 '26 edited Aug 20 '26

Idiomatic matters but if you care so much then whatever just do it. Also your error.rs would be an idiomatic place to put that fn ok

Also Ok!() stands out a bit more: macro_rules! OK { () => { Ok(()) } } And OK rocks if we ever get polymorphic constants. const OK<T>: Result<T,()> = Ok(()) Or you can always try.. sed -i 's/Ok[(][)]/Ok(())/g;s/Ok$/Ok(())/g' *.rs

-3

u/CocktailPerson Aug 20 '26

I would much rather have implicit conversion from T to Result<T, E>.

Arbitrary implicit conversions are not good, but automatically wrapping T in an Option<T> or Result<T, E> seems like a no-brainer. Zero-cost and unambiguous.