Rust by Example - страница 38

стр.

Of course traits can also be generic. Here we define one which reimplements the Drop trait as a generic method to drop itself and an input.

>// Non-copyable types.

>struct Empty;

>struct Null;

>// A trait generic over `T`.

>trait DoubleDrop {

>// Define a method on the caller type which takes an

>// additional single parameter `T` and does nothing with it.

>fn double_drop(self, _: T);

>}

>// Implement `DoubleDrop` for any generic parameter `T` and

>// caller `U`.

>impl DoubleDrop for U {

>// This method takes ownership of both passed arguments,

>// deallocating both.

>fn double_drop(self, _: T) {}

>}

>fn main() {

>let empty = Empty;

>let null = Null;

>// Deallocate `empty` and `null`.

>empty.double_drop(null);

>//empty;

>//null;

>// ^ TODO: Try uncommenting these lines.

>}

>הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

When working with generics, the type parameters often must use traits as bounds to stipulate what functionality a type implements. For example, the following example uses the trait Display to print and so it requires T to be bound by Display; that is, T must implement Display.

>// Define a function `printer` that takes a generic type `T` which

>// must implement trait `Display`.

>fn printer(t: T) {

>println!("{}", t);

>}

Bounding restricts the generic to types that conform to the bounds. That is:

>struct S(T);


>// Error! `Vec` does not implement `Display`. This

>// specialization will fail.

>let s = S(vec![1]);

Another effect of bounding is that generic instances are allowed to access the methods of traits specified in the bounds. For example:

>// A trait which implements the print marker: `{:?}`.

>use std::fmt::Debug;

>trait HasArea {

>fn area(&self) -> f64;

>}

>impl HasArea for Rectangle {

>fn area(&self) -> f64 { self.length * self.height }

>}

>#[derive(Debug)]

>struct Rectangle { length: f64, height: f64 }

>#[allow(dead_code)]

>struct Triangle { length: f64, height: f64 }

>// The generic `T` must implement `Debug`. Regardless

>// of the type, this will work properly.

>fn print_debug(t: &T) {

>println!("{:?}", t);

>}

>// `T` must implement `HasArea`. Any type which meets

>// the bound can access `HasArea`'s function `area`.

>fn area(t: &T) -> f64 { t.area() }

>fn main() {

>let rectangle = Rectangle { length: 3.0, height: 4.0 };

>let _triangle = Triangle { length: 3.0, height: 4.0 };

>print_debug(&rectangle);

>println!("Area: {}", area(&rectangle));

>//print_debug(&_triangle);

>//println!("Area: {}", area(&_triangle));

>// ^ TODO: Try uncommenting these.

>// | Error: Does not implement either `Debug` or `HasArea`.

>}

>הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

As an additional note, where clauses can also be used to apply bounds in some cases to be more expressive.

A consequence of how bounds work is that even if a trait doesn't include any functionality, you can still use it as a bound. Eq and Copy are examples of such traits from the std library.

>struct Cardinal;

>struct BlueJay;

>struct Turkey;

>trait Red {}