This week we will talk about **trait** and **polymorphism**.
## Generic types
Copyright By PowCoder代写 加微信 powcoder
Every programming language has tools for effectively handling the duplication of concepts. In Rust, one such tool is generics. Generics are abstract stand-ins for concrete types or other properties. We have seen many examples of generics, such as `Option
The purpose of using Generic Data Types is reducing duplication of code. To understand how important generics is, imagine that you need to define a `Option` enum for every single data type you have…
### Generic in struct/enum
Without generic, you need to define `Option` for every single data type you want to use
enum Option
Some(u32),
enum Option
Some(u32),
// … No!
As smart coders, we cannot let it happen! So, we can tell rust that we have a generic type `T`, and ask rust to take care of the rest.
enum Option
The `Result` enum is generic over two types, `T` and `E`, and has two variants: `Ok`, which holds a value of type `T`, and `Err`, which holds a value of type `E`.
enum Result
If we want the fields in our custom struct to be generic:
struct Point
impl
fn x(&self) -> &T {
fn main() {
let both_integer = Point { x: 5, y: 10 };
let both_float = Point { x: 1.0, y: 4.0 };
let integer_and_float = Point { x: 5, y: 4.0 };
Note that we have to declare `
Of course, we could implement methods only on some concrete type rather than the generic type
impl Point
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
This code means the type `Point
### Generic in function definitions
In this example, we have a unit struct `A`, a concrete struct `S` and a generic type struct `SGen`.
– Function `reg_fn(_s: S) {}` is not a generic function as there is no `<>`.
– Function `gen_spec_t(_s: SGen) {}` is also not a generic function, because `SGen` is a concrete type.
– Function `gen_spec_i32(_s: SGen
– Function `generic
Let’s see a concrate example:
struct A; // Concrete type `A`.
struct S(A); // Concrete type `S`.
struct SGen
// The following functions all take ownership of the variable passed into
// them and immediately go out of scope, freeing the variable.
// Define a function `reg_fn` that takes an argument `_s` of type `S`.
// This has no `
fn reg_fn(_s: S) {}
// Define a function `gen_spec_t` that takes an argument `_s` of type `SGen
// It has been explicitly given the type parameter `A`, but because `A` has not
// been specified as a generic type parameter for `gen_spec_t`, it is not generic.
fn gen_spec_t(_s: SGen) {}
// Define a function `gen_spec_i32` that takes an argument `_s` of type `SGen
// It has been explicitly given the type parameter `i32`, which is a specific type.
// Because `i32` is not a generic type, this function is also not generic.
fn gen_spec_i32(_s: SGen
// Define a function `generic` that takes an argument `_s` of type `SGen
// Because `SGen
fn generic
fn main() {
// Using the non-generic functions
reg_fn(S(A)); // Concrete type.
gen_spec_t(SGen(A)); // Implicitly specified type parameter `A`.
gen_spec_i32(SGen(6)); // Implicitly specified type parameter `i32`.
// Explicitly specified type parameter `char` to `generic()`.
generic::
// Implicitly specified type parameter `char` to `generic()`.
generic(SGen(‘c’));
### Performance of Code Using Generics
Fortunately, Rust implements generics in such a way that your code doesn’t run any slower using generic types than it would with concrete types. Rust accomplishes this by performing monomorphization of the code that is using generics at compile time. Monomorphization is the process of turning generic code into specific code by filling in the concrete types that are used when compiled.
Let’s look at how this works with an example that uses the standard library’s `Option
fn main() {
let integer = Some(5);
let float = Some(5.0);
When you run this code, *our magic Rust compiler will help you write the monomorphized version of the code:*
enum Option_i32 {
Some(i32),
enum Option_f64 {
Some(f64),
fn main() {
let integer = Option_i32::Some(5);
let float = Option_f64::Some(5.0);
### Trait can help to define the type of generic type
Imagine we are working on many many different type of numbers, and we want to implement a `the_large_one()` function that can return the largest value of two input values.
Without generic, we need to define this function for every single data type:
fn the_large_one(x: i8, y:i8) {if x > y {x} else {y}};
fn the_large_one(x: i16, y:i16) {if x > y {x} else {y}};
As a smart coder, we cannot let it happen. So, we define a generic type `T`, and tell rust to do the rest for us.
fn the_large_one
However, rust will complain because not every data type can be compared.
error[E0369]: binary operation `>` cannot be applied to type `T`
–> src/main.rs:1:44
1 | fn the_large_one
| – ^ – T
| |
| T
help: consider restricting type parameter `T`
1 | fn the_large_one
| ^^^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc –explain E0369`.
How to fix this error? We need trait to tell rust what is our generic type `T`!
A *trait* tells the Rust compiler about functionality a particular **type** has and can share with other types.
– We can use traits to define shared behavior in an abstract way.
– We can use trait bounds to specify that a generic type can be any type that has certain behavior.
## Defining a trait
A type’s behavior consists of the methods we can call on that type. Different types share the same behavior if we can call the same methods on all of those types. *Trait definitions are a way to group method signatures together to define a set of behaviors necessary to accomplish some purpose.*
For example, let’s say we have multiple structs that hold various kinds and amounts of text: a `NewsArticle` struct that holds a news story filed in a particular location and a `Tweet` that can have at most 280 characters along with metadata that indicates whether it was a new tweet, a retweet, or a reply to another tweet.
We want to make a media aggregator library that can display summaries of data that might be stored in a `NewsArticle` or `Tweet` instance. To do this, we need a summary from each type, and we need to request that summary by calling a `summarize` method on an instance.
pub trait Summary {
fn summarize(&self) -> String;
fn say_hello() {
println!(“Hello”)
Here, we declare a `trait` using the `trait` keyword and then the trait’s name, which is `Summary` in this case. Inside the curly brackets, we declare the method signatures that describe the behaviors of the types that implement this trait, which in this case is `fn summarize(&self) -> String`.
We don’t give the function body to `summarize`, because we want the `struct`s that implement this trait to implement their own `summarize` function. However, we give a default implementation to function `say_hello()`, so that the concrete type can choose to use this default implementation, or implement their own.
## Implementing a Trait on a Type
If we want to define the desired behavior of a struct or any type of a trait, we need to implement the trait for the type.
pub trait Summary {
fn summarize(&self) -> String;
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!(“{}, by {} ({})”, self.headline, self.author, self.location)
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
impl Summary for Tweet {
fn summarize(&self) -> String {
format!(“{}: {}”, self.username, self.content)
If you want to call the `Summary` trait from other modules, you need to use `use` keyword as you do for your struct or module. You will also need to specify that `Summary` is a public trait before calling from other modules by saying `pub trait Summary {}`.
You have the choice to provide a default implementation for the desired behavior, like what we did for the `say_hello()` function. Suppose we also give `summarize()` a default implementation, then you just need to say `impl Summary for NewsArticle {}` if you want `NewsArticle` to use the default implementation. Of course you can replace the default implementation using your custom implementation.
## Traits as Parameters
We can use traits in function definition as the parameter type to tell rust this function can take multiple type. For example, if we want to write a function that takes a struct that implement the `Summary` trait as the input, we can say
pub fn notify(item: &impl Summary) {
println!(“Breaking news! {}”, item.summarize());
### Trait Bound Syntax
the `impl Trait` syntax is actually syntax sugar for a long form, which is called a trait bound. Trait bound is a way to set a bound for the types that can be used in functions or methods.
pub fn notify
println!(“Breaking news! {}”, item.summarize());
The `impl Trait` syntax is convenient and makes for more concise code in simple cases. The trait bound syntax can express more complexity in other cases. For example, we can have two parameters that implement `Summary`. Using the `impl Trait` syntax looks like this:
pub fn notify(item1: &impl Summary + Display, item2: &impl Summary + Display) {}
pub fn notify
We can also specify more than one trait bound using the `+` syntax.
### Clearer Trait Bounds with where Clauses
If you have really fancy trait bounds for your types, you function signature will be very very long. To make our life easier, rust defines a **`where` clause** in which you can put all your trait bounds inside.
fn some_function
fn some_function
where T: Display + Clone,
U: Clone + Debug
If you want to use `impl Trait` syntax in the return position to return a value of some type that implements a trait, the function must have a fixed return type. For example, you cannot do `if true {NewsArticle} else {Tweet}`, even though both of them implemented `Summary`. However, we can do some tricks to achieve this. We will talk about how to achieve this with **trait object**.
## Returning Traits with `dyn`
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. So if we want to use a custom trait as the return type of your function, you need to use some trick.
Here we use `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 {})
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());
### Fixing the largest Function with Trait Bounds
fn the_large_one
## Finer controls!
### Using Trait Bounds to Conditionally Implement Methods
We can implement methods conditionally for types that implement a specific trait.
use std::fmt::Display;
struct Pair
impl
fn new(x: T, y: T) -> Self {
Self { x, y }
impl
fn cmp_display(&self) {
if self.x >= self.y {
println!(“The largest member is x = {}”, self.x);
println!(“The largest member is y = {}”, self.y);
We can also conditionally implement a trait *for any type that implements another trait*. Implementations of a trait on any type that satisfies the trait bounds are called *blanket implementations* and are extensively used in the Rust standard library.
For example, the standard library implements the `ToString` trait on **any** type that implements the `Display` trait. It means we can call the `to_string` method defined by the `ToString` trait on any type that implements the `Display` trait. The `impl` block in the standard library looks similar to this code:
impl
// –snip–
See more example in (RBE)[https://doc.rust-lang.org/rust-by-example/trait.html].
### Derivable traits
The smart compiler provides basic implementations for some traits via the `#[derive]` [attribute](https://doc.rust-lang.org/rust-by-example/attribute.html). We can plug and use those traits without thinking about the implementation. *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);
if foot.to_centimeters() < meter {
println!("One foot is {} than one meter.", cmp);
## Operator Overloading
Some operators can be overloaded by trait. 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.
use std::ops;
struct Foo;
struct Bar;
#[derive(Debug)]
struct FooBar;
#[derive(Debug)]
struct BarFoo;
impl ops::Add
type Output = FooBar;
fn add(self, _rhs: Bar) -> FooBar {
println!(“> Foo.add(Bar) was called”);
impl ops::Add
type Output = BarFoo;
fn add(self, _rhs: Foo) -> BarFoo {
println!(“> Bar.add(Foo) was called”);
fn main() {
println!(“Foo + Bar = {:?}”, Foo + Bar);
println!(“Bar + Foo = {:?}”, Bar + Foo);
### Implement iterator for your own trait
The `Iterator` trait is used to implement iterators over collections such as arrays.
The trait requires only a method to be defined for the `next` element, which may be manually defined in an `impl` block or automatically defined (as in arrays and ranges).
As a point of convenience `for` common situations, the for construct turns some collections into iterators using the `.into_iter()` method.
struct Fibonacci {
curr: u32,
next: u32,
impl Iterator for Fibonacci {
// We can refer to this type using Self::Item
type Item = u32;
// Here, we define the sequence using `.curr` and `.next`.
// The return type is `Option
// * When the `Iterator` is finished, `None` is returned.
// * Otherwise, the next value is wrapped in `Some` and returned.
// We use Self::Item in the return type, so we can change
// the type without having to update the function signatures.
fn next(&mut self) -> Option
let new_next = self.curr + self.next;
self.curr = self.next;
self.next = new_next;
// Since there’s no endpoint to a Fibonacci sequence, the `Iterator`
// will never return `None`, and `Some` is always returned.
Some(self.curr)
// Returns a Fibonacci sequence generator
fn fibonacci() -> Fibonacci {
Fibonacci { curr: 0, next: 1 }
fn main() {
// `0..3` is an `Iterator` that generates: 0, 1, and 2.
let mut sequence = 0..3;
println!(“Four consecutive `next` calls on 0..3”);
println!(“> {:?}”, sequence.next());
println!(“> {:?}”, sequence.next());
println!(“> {:?}”, sequence.next());
println!(“> {:?}”, sequence.next());
// `for` works through an `Iterator` until it returns `None`.
// Each `Some` value is unwrapped and bound to a variable (here, `i`).
println!(“Iterate through 0..3 using `for`”);
for i in 0..3 {
println!(“> {}”, i);
// The `take(n)` method reduces an `Iterator` to its first `n` terms.
println!(“The first four terms of the Fibonacci sequence are: “);
for i in fibonacci().take(4) {
println!(“> {}”, i);
// The `skip(n)` method shortens an `Iterator` by dropping its first `n` terms.
println!(“The next four terms of the Fibonacci sequence are: “);
for i in fibonacci().skip(4).take(4) {
println!(“> {}”, i);
let array = [1u32, 3, 3, 7];
// The `iter` method produces an `Iterator` over an array/slice.
println!(“Iterate the following array {:?}”, &array);
for i in array.iter() {
println!(“> {}”, i);
程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com