问题
I'd like to make an immutable, by-reference data type with addition a bit like this:
use std::ops::Add;
struct Point {
x: i64,
y: i64,
}
impl<'a> Add for &'a Point {
type Output = Point;
fn add(self, other: &Point) -> Point {
Point {
x: self.x + &other.x,
y: self.y + &other.y,
}
}
}
How do I implement the Add trait for a reference to a struct? suggests implementing Add
on the reference type.
I can do this, where a
and b
are Point
s:
let c = &a + &b;
let d = &c + &b;
but not this:
let d = &a + &b + &b;
I don't mind the &
signs, but not being able to chain adds doesn't look good to me. I want to implement multiplication (z = a + b * c;
or if I must, z = &a + &b * &c;
), it would look clearer if I didn't have to create a temporary variable.
Is there a way to get this to work cleanly? Bracketing doesn't seem to help.
I understand what's going on, &a + &b
gives a Point
not a &Point
, which suggests that I could implement both add(&Point, Point)
and add(&Point, &Point)
- but now there are 4 cases in total to get all combinations working since a + b * c
and a * b + c
have different precedence / parse trees after all. Is there a nicer way?
I'd also like to avoid unnecessary copying in the non-reference versions. I'm returning a new object anyway, so cloning the inputs first seems a waste of time to me.
回答1:
I would probably just implement Add
of a reference to a value:
impl<'a> Add<&'a Point> for Point {
type Output = Point;
fn add(self, other: &'a Point) -> Point {
Point {
x: self.x + &other.x,
y: self.y + &other.y,
}
}
}
Bracketing doesn't seem to help.
You can take a reference to the result of part of the expression:
let d = &(&a + &b) + &b;
I'm not sure if that looks better or not to you.
now there are 4 cases in total
There's actually 4 cases per operation: (T, T)
, (T, &T)
, (&T, T)
, (&T, &T)
.
Is there a nicer way?
Not really, but that's because of your next requirement...
I'd also like to avoid unnecessary copying in the non-reference versions
That's why the operations consume by value, to allow you to reuse any potential allocations. In the case of the Point
, it's (ahem) pointless because the structs are so small. It's more believable for types like Vec
.
All that being said, people usually use a macro to avoid the drudgery of writing the same thing over and over. For example, the standard library has a macro that assumes the types implement Copy. If you want to reuse allocation, however, that means you do not want all 4 implementations to be the same. At best you might be able to write two implementations (&T, &T)
and (T, &T)
and forward from the remaining two variants.
See also:
- How do I implement the Add trait for a reference to a struct?
来源:https://stackoverflow.com/questions/45174458/how-can-i-implement-an-operator-like-add-for-a-reference-type-so-that-i-can-add