Выбрать главу

functions and structs

Similar to functions, implementations require care to remain generic.

#![allow(unused)]

fn main() {

struct S; // Concrete type `S`

struct GenericVal<T>(T); // Generic type `GenericVal`

// impl of GenericVal where we explicitly specify type parameters:

impl GenericVal<f32> {} // Specify `f32`

impl GenericVal<S> {} // Specify `S` as defined above

// `<T>` Must precede the type to remain generic

impl<T> GenericVal<T> {}

}

struct Val {

vaclass="underline" f64,

}

struct GenVal<T> {

gen_vaclass="underline" T,

}

// impl of Val

impl Val {

fn value(&self) -> &f64 {

&self.val

}

}

// impl of GenVal for a generic type `T`

impl<T> GenVal<T> {

fn value(&self) -> &T {

&self.gen_val

}

}

fn main() {

let x = Val { vaclass="underline" 3.0 };

let y = GenVal { gen_vaclass="underline" 3i32 };

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

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

functions returning references, impl, and struct

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

// 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<T>` for any generic parameter `T` and

// caller `U`.

impl<T, U> DoubleDrop<T> 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

Drop, struct, and trait

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: Display>(t: T) {

println!("{}", t);

}

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

struct S<T: Display>(T);

// Error! `Vec<T>` 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: Debug>(t: &T) {

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