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
>// caller `U`.
>impl
>// 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
>println!("{}", t);
>}
Bounding restricts the generic to types that conform to the bounds. That is:
>struct S
>// Error! `Vec
>// 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
>println!("{:?}", t);
>}
>// `T` must implement `HasArea`. Any type which meets
>// the bound can access `HasArea`'s function `area`.
>fn 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 {}