The trouble is that it's hard to see how you're coming to any of the conclusions that you are reaching. Your statement about empty traits is the easiest thing to respond to because it provides the clearest indication of where there may be a misunderstanding; you seem to be equating traits with types.
Empty garbage interfaces are used to plug in holes in other languages and that was the point. It wasn't a statement about traits in Rust, the subject of the thread has moved on to be more about is function overloading needed in programming, is it just some misconception. Addressing the one low hanging fruit and walking away patting your hands isn't an appropriate response, so I asked for the other points to be addressed.
I fail to see how this is any different from introducing a trait for that generic function.
I would consider the Eval
trait to be a garbage trait, offering no real insight into the design surface of the program and is only there to be a plug to make up for function overloading. That match statement is a potential source of bugs and I think this is better
fn eval(e: ExprLocal...) { ... }
fn eval(e: ExprConst...) { ... }
fn eval(e: ExprAdd...) { ... }
fn eval(e: ExprMul...) { ... }
I also just want to respond to this, because I really don't see where you're coming from calling match
brittle. if
and if let
, totally, but not match
! Here's my typical workflow for adding a new choice for some setting in my program's configuration file:
Control flow can and will find its way into match arms. Enums can have properties that may need to be checked in control flow. Even the venerable Option
.
match result {
Ok(v) if v > some_val => { ... },
Ok(v) { ... },
None if should_continue => { ... },
None => { ... },
}
Pattern matching does not eliminate control flow if it was needed to begin with. And I've seen some gnarly match statements using every possible guard syntax. I only just used an if
in the example.
Regardless, match statements alone can be brittle when trying to make up for a lack of function overloading because match statements have escape hatches for non exhaustive checking.
enum A {
One,
Two,
Three
}
struct S;
impl S {
fn foo(&self, a: A) {
match a {
// honestly each of these => {} curly braces are just functions waiting to get out
A::One => {...},
A::Two => {...},
_ => {...}
}
}
}
// sometime later we include a new variant in A
enum A {
One,
Two,
Three,
Four
}
fn main() {
......
......
let four = A::Four;
our_struct.foo(four); // Nothing happens, _ => {} match arm fall through, we didn't expect this.
}
// with function overloading and variant types
impl S {
fn foo(&self, o: A::One) {
....
}
fn foo(&self, o: A::Two) {
....
}
}
fn main() {
......
......
let four = A::Four;
our_struct.foo(four); // error
}
This is a dead simple example of brittleness caused by the gap of not having function overloading. Per the definition from wikipedia software brittleness is the increased difficulty in fixing older software that may appear reliable, but fails badly when presented with unusual data or altered in a seemingly minor way.
In any case, match statements are still control flow. You and I can and will screw it up. You are trying to constrain the surface area of possible types inside an area and trigger logic for each square foot in that surface. Why not just constrain each area to one possible square foot. I don't understand how 6 functions with match statements scrubbing out types is better than 6 functions with highly constrained types. We only talked about enums here, this gets ridiculously more in favor of function overloading when we start using examples with primitives or any other non variadic data structures defined by their values. Say hello to your good friend if
and else
. I hope it has a unit test.