Rust by Example — страница 28 из 66

` 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

See also:

Drop, struct, and trait

Bounds

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.

See also:

std::fmt, structs, and traits

Testcase: empty bounds

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 {}

trait Blue {}

impl Red for Cardinal {}

impl Blue for BlueJay {}

// These functions are only valid for types which implement these

// traits. The fact that the traits are empty is irrelevant.

fn red(_: &T)   ->&'static str { "red" }

fn blue(_: &T) ->&'static str { "blue" }

fn main() {

let cardinal = Cardinal;

let blue_jay = BlueJay;

let _turkey   = Turkey;

// `red()` won't work on a blue jay nor vice versa

// because of the bounds.

println!("A cardinal is {}", red(&cardinal));

println!("A blue jay is {}", blue(&blue_jay));

//println!("A turkey is {}", red(&_turkey));

// ^ TODO: Try uncommenting this line.

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

See also:

std::cmp::Eq, std::marker::Copy, and traits

Multiple bounds

Multiple bounds can be applied with a +. Like normal, different types are separated with ,.

use std::fmt::{Debug, Display};

fn compare_prints(t: &T) {

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

println!("Display: `{}`", t);

}

fn compare_types(t: &T, u: &U) {

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

println!("u: `{:?}`", u);

}

fn main() {

let string = "words";

let array = [1, 2, 3];

let vec = vec![1, 2, 3];

compare_prints(&string);

//compare_prints(&array);

// TODO ^ Try uncommenting this.