(As you likely know) Normally in Rust the trait is a gentleman's agreement. If my type does not have total Equivalence then I don't implement the Eq trait, and so if a type has Eq then it's promising to deliver total Equivalence. You can, in fact, just defy this and make types that do something "completely wrong" while implementing the syntax of the Trait and I provide a suite of them so that other people don't need to do that experiment:
However, Rust does have two other tricks up its sleeve:
1. Unsafe traits. You can declare a trait to be "unsafe". Implementations of this trait likewise need that "unsafe" keyword and it alerts the programmer that, as with unsafe function calls, they are responsible for actually getting the implementation details correct. Not many standard library traits are unsafe because that's a considerable burden, but a few are, including Send and Sync.
You wouldn't learn much by implementing unsafe traits wrongly in a library like misfortunate, it's the same lesson as a C++ library that scribbles on random memory addresses.
2. Rust knows whether you implemented a trait by hand or merely derived it. In a few cases it can be reasonable for the language to distinguish these cases for built-in traits, since in the former case it knows the derived trait does what is needed even though a manual implementation may not.
It is possible that future improvements to Const Generics will rely on this latter idea. So long as you derive Eq rather than implementing it, Rust can reason that your type really is suitable as a constant type parameter whereas misfortunate::Always is not suitable (it would cause mayhem if permitted because of its idea of what "equality" is) despite claiming to be Eq.
> Rust knows whether you implemented a trait by hand or merely derived it. In a few cases it can be reasonable for the language to distinguish these cases for built-in traits, since in the former case it knows the derived trait does what is needed
Too late to edit. This should clearly have said,
in the latter case not the former case. In the hand implemented case the compiler has no idea whether your implementation has the desired properties.
There needs to be some thought into how it works with Enums too. Rust currently has no built in standard for enum defaults (you have to implement default yourself). It would be nice to somehow mark one of the variants to be represented with the 0 discriminant (Like how Option::None is implemented)
Yes, I can see uses for this. Forty-two is obviously not something you'd actually want, but I can see needing numbers in the range 0-100 and being frustrated that since I need 0 I can't carve out a niche even though I don't mind losing 101 through 255 from a u8 instead of making my type wider.
Still the existing tricks get a lot done for relatively little work. I have used Option<NonZeroUsize> to do roughly what I'd use a single integer for in C, but making explicit that zero isn't just zero, but "I dunno, invalid". Would I have done that even if it cost more space? Probably, but it was cool that I didn't even need to consider that, "Zero cost abstraction".
Yeah something like this would be how I would like to see it, the trait is already there and is pretty much that (without the Copy bound though as that is not needed.) Hopefully it will end up public and can be implemented with a derive.
https://crates.io/crates/misfortunate
However, Rust does have two other tricks up its sleeve:
1. Unsafe traits. You can declare a trait to be "unsafe". Implementations of this trait likewise need that "unsafe" keyword and it alerts the programmer that, as with unsafe function calls, they are responsible for actually getting the implementation details correct. Not many standard library traits are unsafe because that's a considerable burden, but a few are, including Send and Sync.
You wouldn't learn much by implementing unsafe traits wrongly in a library like misfortunate, it's the same lesson as a C++ library that scribbles on random memory addresses.
2. Rust knows whether you implemented a trait by hand or merely derived it. In a few cases it can be reasonable for the language to distinguish these cases for built-in traits, since in the former case it knows the derived trait does what is needed even though a manual implementation may not.
It is possible that future improvements to Const Generics will rely on this latter idea. So long as you derive Eq rather than implementing it, Rust can reason that your type really is suitable as a constant type parameter whereas misfortunate::Always is not suitable (it would cause mayhem if permitted because of its idea of what "equality" is) despite claiming to be Eq.