warning: function cannot return without recursing
--> /Users/edmund/Programming-Local/tnc-analysis/lib/src/lib.rs:83:5
|
83 | fn zero() -> Self {
| ^^^^^^^^^^^^^^^^^ cannot return without recursing
84 | Zero::zero()
| ------------ recursive call site
|
= help: a `loop` may express intention better if this is on purpose
= note: `#[warn(unconditional_recursion)]` on by default
The app hangs when the code depends on Zero.
In general, I'm not sure how I would implement Zero for an enum...
Because I can't find the variant, should I think about choosing a zero that can be cast to whatever is required when I need a zero? (i.e., for all the variants?)
That's some question about your overall goal which I'm not in a position to answer.
I don't know what you mean by ref, but the compiler sees
impl Zero for AnyNumber {
fn zero() -> Self {
Zero::zero() // <---
}
}
And thinks "hmm I need some implementor of Zero where Zero::zero returns AnyNumber, or if that doesn't work, something that can coerce to AnyNumber."
Given the signature of Zero::zero, it determines that it's looking for the implementation of Zero by AnyNumber, since that's a Zero::zero that returns AnyNumber.
It has thus determined that you want to call <AnyNumber as Zero>::zero (recursively).
On a higher level of reasoning, of course this can't do anything reasonable:
impl Zero for AnyNumber {
fn zero() -> Self {
Zero::zero()
}
}
As you never specify which variant the output should be (and Rust isn't the type of language to arbitrarily choose one or have every type nullable, etc).
then an implementation of Zero seems pretty straight forward. Which value of MyI32 would fulfill the laws stated above? Well it's MyI32(0). Of course only if you also implement Add and PartialEq for MyI32.
This will now fulfill the laws for the additive identity.
let a = MyI32(<some i32>)
assert(MyI32::zero() + a == a);
assert(a + MyI32::zero() == a);
Again, given that you also implement Add and PartialEq in a sensible manner.
But for your enum, which Variant should be the zero?
It could be AnyNumber::Uint8(0) but it could also be AnyNumber::Uint16(0) or any of the other variants. So not knowing what exactly you wanna do or what operations AnyNumber supports (i.e. can they be added, multiplied, equated and how does it work with operations between variants?) I would guess that Zero is probably not a sensible thing to implement here.
You could also consider adding Zero (and maybe One) as a fieldless enum-variant.
Consider whether you really want an enum though. The enum allows you to work with different types at runtime. But if the type can be chosen at compile-time, then generics may be an alternative.
It is really unclear what you are wanting. There's nothing in the signature or the types that could possibly lead to a decision as to which variant should be returned.
Maybe what you really want is a generic Zero<T> trait parameterized on a type that contains some information as to the desired variant?
That's a good point and something I did consider. I actually tried to use a generic but ran into the trait not being object safe (e.g., traits like Add use self; and by definition of what I'm doing the memory size will be different (unknown at compile time) without more indirection). I could not find a way around that hurdle; although I suspect there is a way.
When you are right, you are right. Barring some way to work-around this (e.g., "a la" using a parent trait to obviate the need for trait aliases), I'm actually starting to believe the implementation of Zero in num_traits made an unfortunate design decision by excluding a reference to self for the zero function (already required for is_zero, and by def of returning Self, already not object safe).
I get how zero is a way to produce a "neutral" value for the addition operation (the "identity element" in a monoid specification). The current design of Zero presumes there is only one identity element per type. However, it misses how a person might use an enum type to host multiple types, each with their own identity value.
Enums and traits are both ways to unify behavior across types. Has the design over-looked how an enum might need an identity element for each variant?
If I'm right, this would be the first time I see real "harm" (if you will :)), in how I see a strong Rust bias towards traits. Traits are great, but enums aren't like the "goto" syntax that should be avoided :))
The whole point of zero() is to construct a new "zero" value with the algebraic properties outlined above. Taking a self parameter in a function that's supposed to construct a new value without any inputs doesn't make any sense.
I am not at all clear on what you're actually trying to accomplish, perhaps a higher level explanation would get better answers.
I get your point more than I did when I first started thinking about the problem. The caller needs to generate zero "from thin air".
Conceptually, I don't think how I'm thinking about it negates that motivation. The idea of "identity" does not happen in a void, but rather a type context that can be retrieved, and in my view required. My model would be "given I have a value/variant x, generate the "identity" value for x".
I don't know how, but "identity" should be able to align with variant. The current design is limited by requiring alignment with type.
... actually, I see more now how it is a constraint that goes deeper than I have thus far understood. The choice to-date to avoid using self is the only way to go without requiring some sort of binary operation be included in the spec.
That's right, but that's because most number types are a single type, so your use case probably looks very much like a niche or an exception from the point of view of num-traits.