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.