Читаем Rust by Example полностью

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

} else {

Box::new(Cow {})

}

}

fn main() {

let random_number = 0.234;

let animal = random_animal(random_number);

println!("You've randomly chosen an animal, and it says {}", animal.noise());

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

<p id="operator_overloading"><strong><a l:href="#operator_overloading">Operator Overloading</a></strong></p>

In Rust, many of the operators can be overloaded via traits. That is, some operators can be used to accomplish different tasks based on their input arguments. This is possible because operators are syntactic sugar for method calls. For example, the + operator in a + b calls the add method (as in a.add(b)). This add method is part of the Add trait. Hence, the + operator can be used by any implementor of the Add trait.

A list of the traits, such as Add, that overload operators can be found in core::ops.

use std::ops;

struct Foo;

struct Bar;

#[derive(Debug)]

struct FooBar;

#[derive(Debug)]

struct BarFoo;

// The `std::ops::Add` trait is used to specify the functionality of `+`.

// Here, we make `Add` - the trait for addition with a RHS of type `Bar`.

// The following block implements the operation: Foo + Bar = FooBar

impl ops::Add for Foo {

type Output = FooBar;

fn add(self, _rhs: Bar) -> FooBar {

println!("> Foo.add(Bar) was called");

FooBar

}

}

// By reversing the types, we end up implementing non-commutative addition.

// Here, we make `Add` - the trait for addition with a RHS of type `Foo`.

// This block implements the operation: Bar + Foo = BarFoo

impl ops::Add for Bar {

type Output = BarFoo;

fn add(self, _rhs: Foo) -> BarFoo {

Перейти на страницу:

Похожие книги