How to properly use for_all!() in rust?

Relevant libs:

use std::ops::sub;
use either::{Either, Left, Right, for_both};

Relevant code:

struct Vec2 {
    x: f32,
    y: f32,
}

Here I implement subtraction for Vec2:

impl Sub for Vec2 {
  type Output = Either<f32, Vec2>;
  fn sub(self, rhs: Self) -> Either<f32, Self>{
    let result = Vec2 {
      x: self.x - rhs.x,
      y: self.y - rhs.y
	};
	match result {
      Vec2 { x: 0f32, y: 0f32 }  => Left(0f32),
      _ => Right(result),
    }
  }
}

I get an error in my main function. Summed up:

fn main() {
    let u = Vec2 { x: 2f32, y: 1f32 };
    let v = Vec2 { x: 1f32, y: 2f32 };

    let res = for_both!(u - v, s => s); // The error appears here
                                 // ^ this specifically
    println!("u - v: {:#?}", res);
}

The error reads
expected 'f32' found struct 'Vec2'. This is found to be of type 'f32'.

I think I am understanding this wrong, I just don't really know how to interpret this error message. It kind of left me more confused than I already up. I looked at the docs again but it's just really not making much sense.

If you go to either::for_both!'s documentation you can see a source link on the top right. Clicking it will bring you to the source code of the macro, which is just:

macro_rules! for_both {
    ($value:expr, $pattern:pat => $result:expr) => {
        match $value {
            $crate::Either::Left($pattern) => $result,
            $crate::Either::Right($pattern) => $result,
        }
    };
}

So what it does is just being a shorthand for matching an expression you give it (u - v in your case) and for each case match the value inside the Either::Left/Either::Right with the pattern you give it (the first s) and then return the expression you give it at the end (the second s). Notably, since this is a match, the expression returned must have the same type on both sides. In your code however s has type f32 in one branch and Vec2 in the other, so this is an error.

What you could do however is to write the println! instead of the second s. This way the expression on both branches evaluate to () (i.e. no value, which is what println! returns) and you receive the expected print because in each branch the correct type is known. Rust Playground

1 Like

The macro is pretty simple, and in your example it basically expands to

let res = match u - v {
    Left(s) => s,
    Right(s) => s,
};

Which is trying to assign both an f32 and a Vec2 to res.

Maybe you meant to:

let res = u - v;
for_both!(res, s => println!("u - v: {s:#?}"));

But Either implements Debug when both it's generic types do, so you can just

    let res = u - v;
    println!("u - v: {res:#?}");

(Both require deriving Debug for your Vec2.)

Playground. (I ignored other preexiting warnings).

1 Like

Wow, I didn't even consider that match statements have to give the same type. Not gonna lie, it's pretty hard to choose which answer is better, however, I think yours is just a tad more insightful. Thank you.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.