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

&self, number_1: &i32, number_2: &i32) -> bool {

(&self.0 == number_1) && (&self.1 == number_2)

}

// Grab the first number.

fn first(&self) -> i32 { self.0 }

// Grab the last number.

fn last(&self) -> i32 { self.1 }

}

// `C` contains `A` and `B`. In light of that, having to express `A` and

// `B` again is a nuisance.

fn difference(container: &C) -> i32 where

C: Contains {

container.last() - container.first()

}

fn main() {

let number_1 = 3;

let number_2 = 10;

let container = Container(number_1, number_2);

println!("Does container contain {} and {}: {}",

&number_1, &number_2,

container.contains(&number_1, &number_2));

println!("First number: {}", container.first());

println!("Last number: {}", container.last());

println!("The difference is: {}", difference(&container));

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

See also:

structs, and traits

Associated types

The use of "Associated types" improves the overall readability of code by moving inner types locally into a trait as output types. Syntax for the trait definition is as follows:


#![allow(unused)]

fn main() {

// `A` and `B` are defined in the trait via the `type` keyword.

// (Note: `type` in this context is different from `type` when used for

// aliases).

trait Contains {

type A;

type B;


// Updated syntax to refer to these new types generically.

fn contains(&self, &Self::A, &Self::B) -> bool;

}

}

Note that functions that use the trait Contains are no longer required to express A or B at all:

// Without using associated types

fn difference(container: &C) -> i32 where

C: Contains { ... }


// Using associated types

fn difference(container: &C) -> i32 { ... }

Let's rewrite the example from the previous section using associated types:

struct Container(i32, i32);

// A trait which checks if 2 items are stored inside of container.

// Also retrieves first or last value.

trait Contains {

// Define generic types here which methods will be able to utilize.

type A;

type B;

fn contains(&self, _: &Self::A, _: &Self::B) -> bool;

fn first(&self) -> i32;

fn last(&self) -> i32;

}

impl Contains for Container {

// Specify what types `A` and `B` are. If the `input` type

// is `Container(i32, i32)`, the `output` types are determined

// as `i32` and `i32`.

type A = i32;

type B = i32;

// `&Self::A` and `&Self::B` are also valid here.

fn contains(&self, number_1: &i32, number_2: &i32) -> bool {

(&self.0 == number_1) && (&self.1 == number_2)

}

// Grab the first number.

fn first(&self) -> i32 { self.0 }

// Grab the last number.

fn last(&self) -> i32 { self.1 }

}

fn difference(container: &C) -> i32 {

container.last() - container.first()

}

fn main() {

let number_1 = 3;

let number_2 = 10;

let container = Container(number_1, number_2);

println!("Does container contain {} and {}: {}",

&number_1, &number_2,

container.contains(&number_1, &number_2));

println!("First number: {}", container.first());

println!("Last number: {}", container.last());

println!("The difference is: {}", difference(&container));

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Phantom type parameters

A phantom type parameter is one that doesn't show up at runtime, but is checked statically (and only) at compile time.

Data types can use extra generic type parameters to act as markers or to perform type checking at compile time. These extra parameters hold no storage values, and have no runtime behavior.

In the following example, we combine std::marker::PhantomData with the phantom type parameter concept to create tuples containing different data types.

use std::marker::PhantomData;

// A phantom tuple struct which is generic over `A` with hidden parameter `B`.

#[derive(PartialEq)] // Allow equality test for this type.

struct PhantomTuple(A,PhantomData);

// A phantom type struct which is generic over `A` with hidden parameter `B`.

#[derive(PartialEq)] // Allow equality test for this type.

struct PhantomStruct { first: A, phantom: PhantomData }

// Note: Storage is allocated for generic type `A`, but not for `B`.

//       Therefore, `B` cannot be used in computations.

fn main() {

// Here, `f32` and `f64` are the hidden parameters.

// PhantomTuple type specified as ``.

let _tuple1: PhantomTuple = PhantomTuple('Q', PhantomData);

// PhantomTuple type specified as ``.

let _tuple2: PhantomTuple = PhantomTuple('Q', PhantomData);

// Type specified as ``.

let _struct1: PhantomStruct = PhantomStruct {

first: 'Q',

phantom: PhantomData,

};

// Type specified as ``.

let _struct2: PhantomStruct = PhantomStruct {

first: 'Q',