English 中文(简体)
Why does Rust ops::add want me to implement `add` with a move?
原标题:
  • 时间:2023-05-31 04:12:56
  •  标签:
  • rust

I m new to rust and going through the Google course for exercises as I go through "the book", I m on one of the exercises and I am needing to implement the + operator. So I go to the docs and see this page on it and immediately wonder about the lack of ampersands in the args to add for the example, quoted below, permalink to playground

pub struct Point {
    x: f64,
    y: f64,
}

impl std::ops::Add<Point> for Point {
    type Output = Point;
    fn add(&self, b: &Point) -> Point {
        Self {
            x: self.x + b.x,
            y: self.y + b.y,
        }
    }
}

Why would I not expect that the arguments to add are immutable references instead of borrows moves? And it s not just the docs, the compiler wants it too,

   Compiling playground v0.0.1 (/playground)
error[E0053]: method `add` has an incompatible type for trait
 --> src/lib.rs:8:12
  |
8 |     fn add(&self, b: &Point) -> Point {
  |            ^^^^^
  |            |
  |            expected `Point`, found `&Point`
  |            help: change the self-receiver type to match the trait: `self`
  |
  = note: expected signature `fn(Point, Point) -> Point`
             found signature `fn(&Point, &Point) -> Point`

For more information about this error, try `rustc --explain E0053`.
error: could not compile `playground` (lib) due to previous error

Why might the creators of Rust enforce that addition (per it s usual definitions) design it this way? I imagine this works well for literals, but I believe that references would work for literals as well too. What am I missing?

问题回答

yes it can be a little confusing at first, what I would recommend is to consider the Point type completely different from the &Point type, in this case you have implemented add for the Point type, and that is fine, but that is not what you wanted to do, in fact just do the same thing for the &Point type as well

use std::ops::Add;

#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

impl Add for &Point { //PAY ATTENTION HERE
    type Output = Point;

    fn add(self, other: &Point) -> Point { //AND HERE
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

fn main(){

    let point1 = Point{x: 1, y: 2};
    let point2 = Point{x: 1, y: 2};
    
    let point3 = point1.add(&point2);
    
    println!("{:?}", point1);
    println!("{:?}", point2);
    println!("{:?}", point3);
}

look at this playground to do some testing

The trick here is that Add has a somewhat hidden generic parameter; the full trait is

pub trait Add<Rhs = Self> {
    type Output;

    // Required method
    fn add(self, rhs: Rhs) -> Self::Output;
}

The Rhs generic is Self by default, i.e., if unspecified. When you write impl Add for Point what you ve actually written, implicitly, is impl Add<Self> for Point, where Rhs=Self. And then it s clear that the method you must implement is add(self, rhs: Self) (or add(self, rhs: Point)). Similarly if you write impl Add for &Point then you must implement add(self, rhs: &Point). (Note that in this case, self is actually of type &Point, not Point, although you won t notice when everything is Copy.)

So you actually have four ways of implementing addition of Points or &Points here: Add for Point (aka Add<Point> for Point), Add<&Point> for Point, Add for &Point (aka Add<&Point> for &Point), and Add<Point> for &Point.

You may want to implement it for both values and references:

use std::ops::Add;

#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

impl Add<&Point> for Point {
    type Output = Point;
    #[inline]
    fn add(self, other: &Point) -> Point {
        self + *other
    }
}

impl Add<Point> for &Point {
    type Output = Point;
    #[inline]
    fn add(self, other: Point) -> Point {
        *self + other
    }
}

impl Add<&Point> for &Point {
    type Output = Point;
    #[inline]
    fn add(self, other: &Point) -> Point {
        *self + *other
    }
}

With this, you would be add references and values and do things like points.iter().sum().





相关问题
Creating an alias for a variable

I have the following code in Rust (which will not compile but illustrates what I am after). For readability purposes, I would like to refer the same string with two different names so that the name of ...

Rust Visual Studio Code code completion not working

I m trying to learn Rust and installed the Rust extension for VSCode. But I m not seeing auto-completions for any syntax. I d like to call .trim() on String but I get no completion for it. I read that ...

热门标签