2

My class notes have code that implements the HasArea trait and prints the area, similar to the Rust Book example. The professor's note is as following:

trait HasArea<T> {
    fn area(& self) -> T,
}

fn print<T: Display, S: HasArea<T>>(s: &S) {
    println!("", s.area());  // println sth must impl Display trait
}

struct Circle {
    x: T,
    y: T,
    r: T,
}

impl <T: Copy + Mul <Output = T>>
    HasArea<T> for Circle<T>  
{
    fn area(&self) -> T {
        self.r * self.r
    }
}

Comparing that to the Rust Book, which uses shape: T as input:

trait HasArea {
    fn area(&self) -> f64;
}

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl HasArea for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

fn print_area<T: HasArea>(shape: T) {
    println!("This shape has an area of {}", shape.area());
}

fn main() {
    let c = Circle {
        x: 0.0f64,
        y: 0.0f64,
        radius: 1.0f64,
    };

    print_area(c);
}

I am not sure why the professor uses s: &S, while the Rust Book uses shape: T. Can anyone help compare when implementing a generic function, when do we pass a input as x: &T and when x: T?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
enaJ
  • 1,565
  • 5
  • 16
  • 29

2 Answers2

5

Can anyone help compare when implementing a generic function, when do we pass a input as x: &T and when x: T?

It's the same as when you would accept x: Foo and x: &Foo. Taking a x: Foo gives the function the ownership of of x, and (unless x is Copy) the caller will not be able to use it again.

In the Rust Book example, print_area takes the ownership of circle, so you cannot use circle after the print_area(circle); line. For example, the following:

fn main() {
    // ...
    print_area(c);
    print_area(c);
}

throws this error:

error[E0382]: use of moved value: `c`
  --> <anon>:29:16
   |
28 |     print_area(c);
   |                - value moved here
29 |     print_area(c);
   |                ^ value used here after move
   |
   = note: move occurs because `c` has type `Circle`, which does not implement the `Copy` trait

In my experience, most of the time you'll want to take : &T (like your Professor's note). Only when you need the ownership of the struct would you need to use : T. The Rust Book probably uses : T for simplicity.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Dogbert
  • 212,659
  • 41
  • 396
  • 397
  • I disagree with *most of the time you'll want to take `&T`*. If a trait is [implemented for all references to items of that type](http://stackoverflow.com/q/28799372/155423), then there's no reason to add *more* indirection. – Shepmaster Nov 01 '16 at 12:36
  • @Shepmaster good point (I wanted to mention it along with `Copy` but forgot). But then again I haven't seen a lot of impls for `&T` compared to `T`. What do you think about the Rust Book's example consuming `T` just to print its area? – Dogbert Nov 01 '16 at 16:23
1

This is not a question of generics but a question of borrowing and ownership. If you want your input to be moved into function then you use T, otherwise if you just want to borrow it you use &T or &mut T.

Relevant book section

Neikos
  • 1,840
  • 1
  • 22
  • 35