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

fn new(name: &'static str) -> Self;

// Instance method signatures; these will return a string.

fn name(&self) ->&'static str;

fn noise(&self) ->&'static str;

// Traits can provide default method definitions.

fn talk(&self) {

println!("{} says {}", self.name(), self.noise());

}

}

impl Sheep {

fn is_naked(&self) -> bool {

self.naked

}

fn shear(&mut self) {

if self.is_naked() {

// Implementor methods can use the implementor's trait methods.

println!("{} is already naked...", self.name());

} else {

println!("{} gets a haircut!", self.name);

self.naked = true;

}

}

}

// Implement the `Animal` trait for `Sheep`.

impl Animal for Sheep {

// `Self` is the implementor type: `Sheep`.

fn new(name: &'static str) -> Sheep {

Sheep { name: name, naked: false }

}

fn name(&self) ->&'static str {

self.name

}

fn noise(&self) ->&'static str {

if self.is_naked() {

"baaaaah?"

} else {

"baaaaah!"

}

}

// Default trait methods can be overridden.

fn talk(&self) {

// For example, we can add some quiet contemplation.

println!("{} pauses briefly... {}", self.name, self.noise());

}

}

fn main() {

// Type annotation is necessary in this case.

let mut dolly: Sheep = Animal::new("Dolly");

// TODO ^ Try removing the type annotations.

dolly.talk();

dolly.shear();

dolly.talk();

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Derive

The compiler is capable of providing basic implementations for some traits via the #[derive] attribute. These traits can still be manually implemented if a more complex behavior is required.

The following is a list of derivable traits:

   • Comparison traits: Eq, PartialEq, Ord, PartialOrd.

   • Clone, to create T from &T via a copy.

   • Copy, to give a type 'copy semantics' instead of 'move semantics'.

   • Hash, to compute a hash from &T.

   • Default, to create an empty instance of a data type.

   • Debug, to format a value using the {:?} formatter.

// `Centimeters`, a tuple struct that can be compared

#[derive(PartialEq, PartialOrd)]

struct Centimeters(f64);

// `Inches`, a tuple struct that can be printed

#[derive(Debug)]

struct Inches(i32);

impl Inches {

fn to_centimeters(&self) -> Centimeters {

let &Inches(inches) = self;

Centimeters(inches as f64 * 2.54)

}

}

// `Seconds`, a tuple struct with no additional attributes

struct Seconds(i32);

fn main() {

let _one_second = Seconds(1);

// Error: `Seconds` can't be printed; it doesn't implement the `Debug` trait

//println!("One second looks like: {:?}", _one_second);

// TODO ^ Try uncommenting this line

// Error: `Seconds` can't be compared; it doesn't implement the `PartialEq` trait

//let _this_is_true = (_one_second == _one_second);

// TODO ^ Try uncommenting this line

let foot = Inches(12);

println!("One foot equals {:?}", foot);

let meter = Centimeters(100.0);

let cmp =

if foot.to_centimeters() < meter {

"smaller"

} else {

"bigger"

};

println!("One foot is {} than one meter.", cmp);

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

See also:

derive

Returning Traits withdyn

The Rust compiler needs to know how much space every function's return type requires. This means all your functions have to return a concrete type. Unlike other languages, if you have a trait like Animal, you can't write a function that returns Animal, because its different implementations will need different amounts of memory.

However, there's an easy workaround. Instead of returning a trait object directly, our functions return a Box which contains some Animal. A box is just a reference to some memory in the heap. Because a reference has a statically-known size, and the compiler can guarantee it points to a heap-allocated Animal, we can return a trait from our function!

Rust tries to be as explicit as possible whenever it allocates memory on the heap. So if your function returns a pointer-to-trait-on-heap in this way, you need to write the return type with the dyn keyword, e.g. Box.

struct Sheep {}

struct Cow {}

trait Animal {

// Instance method signature

fn noise(&self) ->&'static str;

}

// Implement the `Animal` trait for `Sheep`.

impl Animal for Sheep {

fn noise(&self) ->&'static str {

"baaaaah!"

}

}

// Implement the `Animal` trait for `Cow`.

impl Animal for Cow {

fn noise(&self) ->&'static str {

"moooooo!"

}

}

// Returns some struct that implements Animal, but we don't know which one at compile time.

fn random_animal(random_number: f64) -> Box {

if random_number < 0.5 {

Box::new(Sheep {})