How to borrow p1 and p2 in example 10-11 in The Rust Book?

Hi, in The Rust Book -> this section -> example 10-11

We have this code:

struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
    fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}

fn main() {
    let p1 = Point { x: 5, y: 10.4 };
    let p2 = Point { x: "Hello", y: 'c' };

    let p3 = p1.mixup(p2);

    println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

Now i want to borrow p1 and p2 without moving them to the mixup method, something like that (which will not compile):

struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
    fn mixup<X2, Y2>(&self, other: &Point<X2, Y2>) -> Point<X1, Y2> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}

fn main() {
    let p1 = Point { x: 5, y: 10.4 };
    let p2 = Point { x: "Hello", y: 'c' };

    let p3 = p1.mixup(&p2);

    println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

Maybe i need a way to clone self and other inside mixup to return a new Point instance ...

Using references for the thing you are trying to achieve doesn't make much sense, since you are quite literally "taking-apart" two Points and making a new Point - effectively destroying the old ones.
If you want to keep around the old points, you can clone them while making the call.

let p1c = p1.clone();
let p2c = p2.clone();
let p3 = p1c.mixup(p2c);
1 Like

You can also just return a Point<&X1, &Y2>:

impl<X1, Y1> Point<X1, Y1> {
    fn mixup_ref<'a, 'b, X2, Y2>(&'a self, other: &'b Point<X2, Y2>) -> Point<&'a X1, &'b Y2> {
        Point {
            x: &self.x,
            y: &other.y,
        }
    }
}
1 Like

To precisely require only what you need, you might want to clone just the elements to be produced:

impl<X1, Y1> Point<X1, Y1> {
    fn mixup<X2, Y2>(&self, other: &Point<X2, Y2>) -> Point<X1, Y2>
    where
        X1: Clone,
        Y2: Clone,
    {
        Point {
            x: self.x.clone(),
            y: other.y.clone(),
        }
    }
}

Note that most actual point coordinates — numbers — are Copy (and therefore also Clone) so this is not a big deal.

2 Likes

Unfortunately this will not work:

struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
    fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}

fn main() {
    let p1 = Point { x: 5, y: 10.4 };
    let p2 = Point { x: "Hello", y: 'c' };

    let p1c = p1.clone();
    let p2c = p2.clone();

    let p3 = p1c.mixup(p2c);

    println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

I get this error:

error[E0599]: no method named `clone` found for struct `Point` in the current scope
  --> src/bin/generics_8.rs:19:18
   |
1  | struct Point<X1, Y1> {
   | -------------------- method `clone` not found for this struct
...
19 |     let p1c = p1.clone();
   |                  ^^^^^ method not found in `Point<{integer}, {float}>`
   |
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `clone`, perhaps you need to implement it:
           candidate #1: `Clone`

error[E0599]: no method named `clone` found for struct `Point` in the current scope
  --> src/bin/generics_8.rs:20:18
   |
1  | struct Point<X1, Y1> {
   | -------------------- method `clone` not found for this struct
...
20 |     let p2c = p2.clone();
   |                  ^^^^^ method not found in `Point<&str, char>`
   |
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `clone`, perhaps you need to implement it:
           candidate #1: `Clone`

For more information about this error, try `rustc --explain E0599`.
error: could not compile `the_book` due to 2 previous errors

Thank you, that's exactly what i wanted to do. Tho i still do not understand what is where but that looks neater that using lifetimes 'a.

Is Clone defined in the so called prelude as you did not bring it into scope ?

It does work.

However types don't automatically implement traits (with a few exceptions). You can only Clone a type that explicitly implements Clone. The easiest way to implement Clone for your Point type is to derive it automatically:

#[derive(Clone)]
struct Point<X, Y> {
    x: X,
    y: Y,
}

Incidentally, it's good practice to derive (or failing that, manually implement) most of the other common/standard traits as well (as long as they make sense) so that other people will find your code easier to use.

It's not "neater", it serves a completely different purpose. Just like a delicious stake is not "more delicious" than a delicious dessert – but sometimes you want one, at other times the other.

Using lifetimes here is actually more general, because it doesn't restrict the set of types to those which are clonable (which also means avoiding spurious clones). In contrast, the where clause does exactly that: it introduces a constraint (that's what where clauses are for) that says the two mentioned types must be cloneable – that's needed because you want to call clone() on them.

However, the Clone solution might be marginally more convenient since you don't have to deal with lifetimes, indeed, even if a bit less general.

2 Likes