How to do operator overloading on enums? - rust

I have an enum:
enum Numbers {
A = 1,
}
How can I overload the operator so that I can compare the Numbers enum with another standard number without have to do as u32? If I want to do:
let a = Numbers::A < 4
I think i read somewhere that it's possible to do with some kind of macros? Maybe there is a crate that lets me do this? So far the only thing I have had success with is as u32 but it gets quite tedious to have to write it out every time.

You do it the exact same way that you overload an operator for a struct: implement the appropriate std::ops trait.
However, you don't really want to overload the meaning of <, you still wish to perform a comparison.
If you run the code you typed, the compiler tells you what to do:
error[E0369]: binary operation `<` cannot be applied to type `Numbers`
--> src/main.rs:6:13
|
6 | let a = Numbers::A < 4;
| ^^^^^^^^^^^^^^
|
= note: an implementation of `std::cmp::PartialOrd` might be missing for `Numbers`
So, implement PartialOrd:
#[derive(Copy, Clone)]
enum Numbers {
A = 1,
}
use std::cmp::Ordering;
impl PartialEq<i32> for Numbers {
fn eq(&self, other: &i32) -> bool {
(*self as i32).eq(other)
}
}
impl PartialOrd<i32> for Numbers {
fn partial_cmp(&self, other: &i32) -> Option<Ordering> {
(*self as i32).partial_cmp(other)
}
}
fn main() {
let a = Numbers::A < 4;
}
As Sven Marnach points out:
It may be worth pointing out that this implementation only allows comparisons with Numbers on the left-hand side and i32 on the right-hand side. Comparisons like 4 > Numbers::A and Numbers.A < Numbers.B would require separate implementations. In addition, since you are dealing with integers, you would also want to implement Ord and Eq, so for all combinations of operands you end up with twelve trait implementations.
Of course, it depends on your exact cases:
if you want to compare Numbers to Numbers, you might be able to #[derive(PartialOrd)].
if you want Eq, you can derive it.
you can write macros to reduce some of the redundancy.
See also:
How do I convert an enum reference to a number?
How do I implement the Add trait for a reference to a struct?

Related

"Unconstrained generic constant" when adding const generics

How would I add const generics? Lets say I have a type foo:
pub struct foo <const bar: i64> {
value: f64,
}
and I want to implement mul so I can multiply 2 foos together. I want to treat bar as a dimension, so foo<baz>{value: x} * foo<quux>{value: k} == foo<baz + quux>{value: x * k}, as follows:
impl<const baz: i64, const quux: i64> Mul<foo<quux>> for foo<baz> {
type Output = foo<{baz + quux}>;
fn mul(self, rhs: foo<quux>) -> Self::Output {
Self::Output {
value: self.value * rhs.value,
}
}
}
I get an error telling me I need to add a where bound on {baz+quux} within the definition of the output type. What exactly does this mean and how do I implement it? I can't find any seemingly relevant information on where.
The solution
I got a variation on your code to work here:
impl<const baz: i64, const quux: i64> Mul<Foo<quux>> for Foo<baz>
where Foo<{baz + quux}>: Sized {
type Output = Foo<{baz + quux}>;
fn mul(self, rhs: Foo<quux>) -> Self::Output {
Self::Output {
value: self.value * rhs.value,
}
}
}
How I got there
I've reproduced the full error that you get without the added where clause below:
error: unconstrained generic constant
--> src/main.rs:11:5
|
11 | type Output = Foo<{baz + quux}>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try adding a `where` bound using this expression: `where [u8; {baz + quux}]: Sized`
Now, the clause that it suggests is not very useful, for one reason: the length parameter of a statically sized slice must be a usize, but our values baz and quux (and their sum) are i64. I'd imagine that the compiler authors included that particular suggestion because the primary use case for const generics is embedding array sizes in types. I've opened an issue on GitHub about this diagnostic.
Why is this necessary?
A where clause specifies constraints on some generic code element---a function, type, trait, or in this case, implementation---based on the traits and lifetimes that one or more generic parameters, or a derivative thereof, must satisfy. There are equivalent shorthands for many cases, but the overall requirement is that the constraints are fully specified.
In our case, it may seem superficially that this implementation works for any combination of baz and quux, but this is not the case, due to integer overflow; if we supply sufficiently large values of the same sign for both, their sum cannot be represented by i64. This means that i64 is not closed under addition.
The constraint that we add requires that the sum of the two values is in the set of possible values of an i64, indirectly, by requiring something of the type which consumes it. Hence, supplying 2^31 for both baz and quux is not valid, since the resulting type Foo<{baz + quux}> does not exist, so it cannot possibly implement the Sized trait. While this technically is a stricter constraint than we need (Sized is a stronger requirement than a type simply existing), all Foo<bar> which exist implement Sized, so in our case it is the same. On the other hand, without the constraint, no where clause, explicit or shorthand, specifies this constraint.

Why do I need to dereference a variable when comparing it but not when doing arithmetic?

I have the following code:
fn example(known_primes: &[i32], number: i32, prime: i32, limit: i32) {
let mut is_prime = true;
for prime in known_primes {
if number % prime == 0 {
is_prime = false;
break;
}
if *prime > limit {
break;
}
}
}
Why do I need to dereference prime in the second condition (*prime > limit), when I don't need to do so in the first one (number % prime == 0)?
Both % and < are operators that take two numbers and return something. The only difference seems to be in what they return (a number vs. a boolean). While Why isn't it possible to compare a borrowed integer to a literal integer? does explain what would be required to make the code work (implementations for all overloads, ideally in the standard library), it does not say why it does work for a % b. Is there a fundamental difference between these operators? Or is it just not implemented yet?
Comparison operators actually do behave differently than arithmetic operators. The difference becomes obvious when looking at the trait definitions. As an example, here is the PartialEq trait
pub trait PartialEq<Rhs = Self>
where
Rhs: ?Sized,
{
fn eq(&self, other: &Rhs) -> bool;
fn ne(&self, other: &Rhs) -> bool { ... }
}
and the Add trait
pub trait Add<RHS = Self> {
type Output;
fn add(self, rhs: RHS) -> Self::Output;
}
We can see that comparison traits take the operands by reference, while the arithmetic traits take the operands by value. This difference is reflected in how the compiler translates operator expressions:
a == b ==> std::cmp::PartialEq::eq(&a, &b)
a + b ==> std::ops::Add::add(a, b)
The operands of comparisons are evaluated as place expressions, so they can never move values. Operands of arithmetic operators, on the other hand, are evaluated as value expressions, so they are moved or copied depending on whether the operand type is Copy.
As a result of this difference, if we implement PartialEq for the type A, we can not only compare A and A, but also &A and &A by virtue of deref coercions for the operands. For Add on the other hand we need a separate implementation to be able to add &A and &A.
I can't answer why the standard library implements the "mixed" versions for reference and value for arithmetic operators, but not for comparisons. I can't see a fundamental reason why the latter can't be done.
Because you can have Rem implementation for different types and the core library implements
impl<'a> Rem<&'a i32> for i32 { /* … */ }
This is impossible for PartialOrd and Ord traits, so you need to compare exactly the same types, in this case i32, that is why there is requirement for dereference.

Is it possible to make my own Box-like wrapper?

I noticed that Box<T> implements everything that T implements and can be used transparently. For Example:
let mut x: Box<Vec<u8>> = Box::new(Vec::new());
x.push(5);
I would like to be able to do the same.
This is one use case:
Imagine I'm writing functions that operate using an axis X and an axis Y. I'm using values to change those axis that are of type numbers but belongs only to one or the other axis.
I would like my compiler to fail if I attempt to do operations with values that doesn't belong to the good axis.
Example:
let x = AxisX(5);
let y = AxisY(3);
let result = x + y; // error: incompatible types
I can do this by making a struct that will wrap the numbers:
struct AxisX(i32);
struct AxisY(i32);
But that won't give me access to all the methods that i32 provides like abs(). Example:
x.abs() + 3 // error: abs() does not exist
// ...maybe another error because I don't implement the addition...
Another possible use case:
You can appropriate yourself a struct of another library and implement or derive anything more you would want. For example: a struct that doesn't derive Debug could be wrapped and add the implementation for Debug.
You are looking for std::ops::Deref:
In addition to being used for explicit dereferencing operations with the (unary) * operator in immutable contexts, Deref is also used implicitly by the compiler in many circumstances. This mechanism is called 'Deref coercion'. In mutable contexts, DerefMut is used.
Further:
If T implements Deref<Target = U>, and x is a value of type T, then:
In immutable contexts, *x on non-pointer types is equivalent to *Deref::deref(&x).
Values of type &T are coerced to values of type &U
T implicitly implements all the (immutable) methods of the type U.
For more details, visit the chapter in The Rust Programming Language as well as the reference sections on the dereference operator, method resolution and type coercions.
By implementing Deref it will work:
impl Deref for AxisX {
type Target = i32;
fn deref(&self) -> &i32 {
&self.0
}
}
x.abs() + 3
You can see this in action on the Playground.
However, if you call functions from your underlying type (i32 in this case), the return type will remain the underlying type. Therefore
assert_eq!(AxisX(10).abs() + AxisY(20).abs(), 30);
will pass. To solve this, you may overwrite some of those methods you need:
impl AxisX {
pub fn abs(&self) -> Self {
// *self gets you `AxisX`
// **self dereferences to i32
AxisX((**self).abs())
}
}
With this, the above code fails. Take a look at it in action.

Providing Blanket Trait Implementations for a Custom Trait

I am implementing a quick geometry crate for practice, and I want to implement two structs, Vector and Normal (this is because standard vectors and normal vectors map through certain transformations differently). I've implemented the following trait:
trait Components {
fn new(x: f32, y: f32, z: f32) -> Self;
fn x(&self) -> f32;
fn y(&self) -> f32;
fn z(&self) -> f32;
}
I'd also like to be add two vectors together, as well as two normals, so I have blocks that look like this:
impl Add<Vector> for Vector {
type Output = Vector;
fn add(self, rhs: Vector) -> Vector {
Vector { vals: [
self.x() + rhs.x(),
self.y() + rhs.y(),
self.z() + rhs.z()] }
}
}
And almost the exact same impl for Normals. What I really want is to provide a default Add impl for every struct that implements Components, since typically, they all will add the same way (e.g. a third struct called Point will do the same thing). Is there a way of doing this besides writing out three identical implementations for Point, Vector, and Normal? Something that might look like this:
impl Add<Components> for Components {
type Output = Components;
fn add(self, rhs: Components) -> Components {
Components::new(
self.x() + rhs.x(),
self.y() + rhs.y(),
self.z() + rhs.z())
}
}
Where "Components" would automatically get replaced by the appropriate type. I suppose I could do it in a macro, but that seems a little hacky to me.
In Rust, it is possible to define generic impls, but there are some important restrictions that result from the coherence rules. You'd like an impl that goes like this:
impl<T: Components> Add<T> for T {
type Output = T;
fn add(self, rhs: T) -> T {
T::new(
self.x() + rhs.x(),
self.y() + rhs.y(),
self.z() + rhs.z())
}
}
Unfortunately, this does not compile:
error: type parameter T must be used as the type parameter for some local type (e.g. MyStruct<T>); only traits defined in the current crate can be implemented for a type parameter [E0210]
Why? Suppose your Components trait were public. Now, a type in another crate could implement the Components trait. That type might also try to implement the Add trait. Whose implementation of Add should win, your crate's or that other crate's? By Rust's current coherence rules, the other crate gets this privilege.
For now, the only option, besides repeating the impls, is to use a macro. Rust's standard library uses macros in many places to avoid repeating impls (especially for the primitive types), so you don't have to feel dirty! :P
At present, macros are the only way to do this. Coherence rules prevent multiple implementations that could overlap, so you can’t use a generic solution.

Error with Rust: cannot call a method whose type contains a self-type through an object

I want to make an instance of Ord, which compares my objects by struct field. Maybe I am missing something here
#[deriving(Eq, Clone)]
struct SortableLine<T>{
comparablePart: ~T,
line: ~str
}
impl Ord for SortableLine<~Ord>{
fn lt(&self, other: &SortableLine<~Ord>) -> bool{
return self.comparablePart.lt(&other.comparablePart);
}
}
This fails with
Thanks
cannot call a method whose type contains a self-type through an object
Is there a way to make ordering of parent object based on ordering of a field comparison?
Your type parameters are the problem; you're trying to use trait objects when that's not what you've actually got or want. Here's how you should implement it: with generics.
#[deriving(Eq, Clone)]
struct SortableLine<T>{
comparable_part: ~T,
line: ~str
}
impl<T: Ord> Ord for SortableLine<T> {
fn lt(&self, other: &SortableLine<T>) -> bool {
return self.comparable_part < other.comparable_part;
}
}
Note two other changes:
I used a < b rather than a.lt(&b). I reckon it's simpler, though it's much less important in an Ord impl.
I changed comparablePart to comparable_part (oh, and spacing in a couple of places) to fit in with standard Rust styles.
This sort of thing has the often-convenient side-effect that you don't need it to be a SortableLine; it can just be a Line, and it will be orderable if it's made of orderable parts, and not if not.

Resources