update repo layout

This commit is contained in:
sunface
2022-03-23 20:04:00 +08:00
parent e2d3027b60
commit c1d8b06518
111 changed files with 958 additions and 4 deletions

View File

@@ -0,0 +1,279 @@
# Advance 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. For example :
```rust
pub trait CacheableItem: Clone + Default + fmt::Debug + Decodable + Encodable {
type Address: AsRef<[u8]> + Clone + fmt::Debug + Eq + Hash;
fn is_null(&self) -> bool;
}
```
Using of `Address` is much more clearable and convenient than `AsRef<[u8]> + Clone + fmt::Debug + Eq + Hash`.
1. 🌟🌟🌟
```rust,editable
struct Container(i32, i32);
// USING associated types to re-implement trait Contains.
// trait Contains {
// type A;
// type B;
trait Contains<A, B> {
fn contains(&self, _: &A, _: &B) -> bool;
fn first(&self) -> i32;
fn last(&self) -> i32;
}
impl Contains<i32, i32> for Container {
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<A, B, C: Contains<A, B>>(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));
}
```
## Default Generic Type Parameters
When we use generic type parameters, we can specify a default concrete type for the generic type. This eliminates the need for implementors of the trait to specify a concrete type if the default type works.
2. 🌟🌟
```rust,editable
use std::ops::Sub;
#[derive(Debug, PartialEq)]
struct Point<T> {
x: T,
y: T,
}
// FILL in the blank in three ways: two of them use the default generic parameters, the other one not.
// Notice that the implementation uses the associated type `Output`.
impl __ {
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
Point {
x: self.x - other.x,
y: self.y - other.y,
}
}
}
fn main() {
assert_eq!(Point { x: 2, y: 3 } - Point { x: 1, y: 0 },
Point { x: 1, y: 3 });
println!("Success!")
}
```
## Fully Qualified Syntax
Nothing in Rust prevents a trait from having a method with the same name as another traits method, nor does Rust prevent you from implementing both traits on one type. Its also possible to implement a method directly on the type with the same name as methods from traits.
When calling methods with the same name, we have to use Fully Qualified Syntax.
#### Example
```rust,editable
trait UsernameWidget {
// Get the selected username out of this widget
fn get(&self) -> String;
}
trait AgeWidget {
// Get the selected age out of this widget
fn get(&self) -> u8;
}
// A form with both a UsernameWidget and an AgeWidget
struct Form {
username: String,
age: u8,
}
impl UsernameWidget for Form {
fn get(&self) -> String {
self.username.clone()
}
}
impl AgeWidget for Form {
fn get(&self) -> u8 {
self.age
}
}
fn main() {
let form = Form{
username: "rustacean".to_owned(),
age: 28,
};
// If you uncomment this line, you'll get an error saying
// "multiple `get` found". Because, after all, there are multiple methods
// named `get`.
// println!("{}", form.get());
let username = UsernameWidget::get(&form);
assert_eq!("rustacean".to_owned(), username);
let age = AgeWidget::get(&form); // you can also use `<Form as AgeWidget>::get`
assert_eq!(28, age);
println!("Success!")
}
```
#### Exercise
3. 🌟🌟
```rust,editable
trait Pilot {
fn fly(&self) -> String;
}
trait Wizard {
fn fly(&self) -> String;
}
struct Human;
impl Pilot for Human {
fn fly(&self) -> String {
String::from("This is your captain speaking.")
}
}
impl Wizard for Human {
fn fly(&self) -> String {
String::from("Up!")
}
}
impl Human {
fn fly(&self) -> String {
String::from("*waving arms furiously*")
}
}
fn main() {
let person = Human;
assert_eq!(__, "This is your captain speaking.");
assert_eq!(__, "Up!");
assert_eq!(__, "*waving arms furiously*");
println!("Success!")
}
```
## Supertraits
Sometimes, you might need one trait to use another traits functionality( like the "inheritance" in other languages ). In this case, you need to rely on the dependent trait also being implemented. The trait you rely on is a `supertrait` of the trait youre implementing.
4. 🌟🌟🌟
```rust,editable
trait Person {
fn name(&self) -> String;
}
// Person is a supertrait of Student.
// Implementing Student requires you to also impl Person.
trait Student: Person {
fn university(&self) -> String;
}
trait Programmer {
fn fav_language(&self) -> String;
}
// CompSciStudent (computer science student) is a subtrait of both Programmer
// and Student. Implementing CompSciStudent requires you to impl both supertraits.
trait CompSciStudent: Programmer + Student {
fn git_username(&self) -> String;
}
fn comp_sci_student_greeting(student: &dyn CompSciStudent) -> String {
format!(
"My name is {} and I attend {}. My favorite language is {}. My Git username is {}",
student.name(),
student.university(),
student.fav_language(),
student.git_username()
)
}
struct CSStudent {
name: String,
university: String,
fav_language: String,
git_username: String
}
// IMPLEMENT the necessary traits for CSStudent to make the code work
impl ...
fn main() {
let student = CSStudent {
name: "Sunfei".to_string(),
university: "XXX".to_string(),
fav_language: "Rust".to_string(),
git_username: "sunface".to_string()
};
// FILL in the blank
println!("{}", comp_sci_student_greeting(__));
}
```
## Orphan Rules
We cant implement external traits on external types. For example, we cant implement the `Display` trait on `Vec<T>` within our own crate, because `Display` and `Vec<T>` are defined in the standard library and arent local to our crate.
This restriction is often called as the orphan rule, so named because the parent type is not present. This rule ensures that other peoples code cant break your code and vice versa.
Its possible to get around this restriction using the newtype pattern, which involves creating a new type in a tuple struct.
5. 🌟🌟
```rust,editable
use std::fmt;
// DEFINE a newtype `Pretty` here
impl fmt::Display for Pretty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{}\"", self.0.clone() + ", world")
}
}
fn main() {
let w = Pretty("hello".to_string());
println!("w = {}", w);
}
```
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it :)

View File

@@ -0,0 +1,145 @@
# Const Generics
Const generics are generic arguments that range over constant values, rather than types or lifetimes. This allows, for instance, types to be parameterized by integers. In fact, there has been one example of const generic types since early on in Rust's development: the array types [T; N], for some type T and N: usize. However, there has previously been no way to abstract over arrays of an arbitrary size: if you wanted to implement a trait for arrays of any size, you would have to do so manually for each possible value. For a long time, even the standard library methods for arrays were limited to arrays of length at most 32 due to this problem.
## Examples
1. Here's an example of a type and implementation making use of const generics: a type wrapping a pair of arrays of the same size.
```rust,editable
struct ArrayPair<T, const N: usize> {
left: [T; N],
right: [T; N],
}
impl<T: Debug, const N: usize> Debug for ArrayPair<T, N> {
// ...
}
```
2. Currently, const parameters may only be instantiated by const arguments of the following forms:
- A standalone const parameter.
- A literal (i.e. an integer, bool, or character).
- A concrete constant expression (enclosed by {}), involving no generic parameters.
```rust,editable
fn foo<const N: usize>() {}
fn bar<T, const M: usize>() {
foo::<M>(); // ok: `M` is a const parameter
foo::<2021>(); // ok: `2021` is a literal
foo::<{20 * 100 + 20 * 10 + 1}>(); // ok: const expression contains no generic parameters
foo::<{ M + 1 }>(); // error: const expression contains the generic parameter `M`
foo::<{ std::mem::size_of::<T>() }>(); // error: const expression contains the generic parameter `T`
let _: [u8; M]; // ok: `M` is a const parameter
let _: [u8; std::mem::size_of::<T>()]; // error: const expression contains the generic parameter `T`
}
fn main() {}
```
3. Const generics can also let us avoid some runtime checks.
```rust
/// A region of memory containing at least `N` `T`s.
pub struct MinSlice<T, const N: usize> {
/// The bounded region of memory. Exactly `N` `T`s.
pub head: [T; N],
/// Zero or more remaining `T`s after the `N` in the bounded region.
pub tail: [T],
}
fn main() {
let slice: &[u8] = b"Hello, world";
let reference: Option<&u8> = slice.get(6);
// We know this value is `Some(b' ')`,
// but the compiler can't know that.
assert!(reference.is_some());
let slice: &[u8] = b"Hello, world";
// Length check is performed when we construct a MinSlice,
// and it's known at compile time to be of length 12.
// If the `unwrap()` succeeds, no more checks are needed
// throughout the `MinSlice`'s lifetime.
let minslice = MinSlice::<u8, 12>::from_slice(slice).unwrap();
let value: u8 = minslice.head[6];
assert_eq!(value, b' ')
}
```
## Exercises
1. 🌟🌟 `<T, const N: usize>` is part of the struct type, it means `Array<i32, 3>` and `Array<i32, 4>` are different types.
```rust,editable
struct Array<T, const N: usize> {
data : [T; N]
}
fn main() {
let arrays = [
Array{
data: [1, 2, 3],
},
Array {
data: [1.0, 2.0, 3.0],
},
Array {
data: [1, 2]
}
];
println!("Success!")
}
```
2. 🌟🌟
```rust,editable
// fill in the blanks to make it work
fn print_array<__>(__) {
println!("{:?}", arr);
}
fn main() {
let arr = [1, 2, 3];
print_array(arr);
let arr = ["hello", "world"];
print_array(arr);
}
```
3. 🌟🌟🌟 Sometimes we want to limit the size of an variable, e.g when using in embedding evironments, then `const expressions` will fit your need.
```rust,editable
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
fn check_size<T>(val: T)
where
Assert<{ core::mem::size_of::<T>() < 768 }>: IsTrue,
{
//...
}
// fix the errors in main
fn main() {
check_size([0u8; 767]);
check_size([0i32; 191]);
check_size(["hello你好"; __]); // size of &str ?
check_size(["hello你好".to_string(); __]); // size of String?
check_size(['中'; __]); // size of char ?
println!("Success!")
}
pub enum Assert<const CHECK: bool> {}
pub trait IsTrue {}
impl IsTrue for Assert<true> {}
```
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it :)

View File

@@ -0,0 +1,156 @@
# Generics
### Functions
1. 🌟🌟🌟
```rust,editable
// fill in the blanks to make it work
struct A; // Concrete type `A`.
struct S(A); // Concrete type `S`.
struct SGen<T>(T); // Generic type `SGen`.
fn reg_fn(_s: S) {}
fn gen_spec_t(_s: SGen<A>) {}
fn gen_spec_i32(_s: SGen<i32>) {}
fn generic<T>(_s: SGen<T>) {}
fn main() {
// Using the non-generic functions
reg_fn(__); // Concrete type.
gen_spec_t(__); // Implicitly specified type parameter `A`.
gen_spec_i32(__); // Implicitly specified type parameter `i32`.
// Explicitly specified type parameter `char` to `generic()`.
generic::<char>(__);
// Implicitly specified type parameter `char` to `generic()`.
generic(__);
println!("Success!")
}
```
2. 🌟🌟 A function call with explicitly specified type parameters looks like: `fun::<A, B, ...>()`.
```rust,editable
// implement the generic function below
fn sum
fn main() {
assert_eq!(5, sum(2i8, 3i8));
assert_eq!(50, sum(20, 30));
assert_eq!(2.46, sum(1.23, 1.23));
println!("Success!")
}
```
### Struct and `impl`
3. 🌟
```rust,editable
// implement struct Point to make it work
fn main() {
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
println!("Success!")
}
```
4. 🌟🌟
```rust,editable
// modify this struct to make the code work
struct Point<T> {
x: T,
y: T,
}
fn main() {
// DON'T modify here
let p = Point{x: 5, y : "hello".to_string()};
println!("Success!")
}
```
5. 🌟🌟
```rust,editable
// add generic for Val to make the code work, DON'T modify the code in `main`
struct Val {
val: f64,
}
impl Val {
fn value(&self) -> &f64 {
&self.val
}
}
fn main() {
let x = Val{ val: 3.0 };
let y = Val{ val: "hello".to_string()};
println!("{}, {}", x.value(), y.value());
}
```
### Method
6. 🌟🌟🌟
```rust,editable
struct Point<T, U> {
x: T,
y: U,
}
impl<T, U> Point<T, U> {
// implement mixup to make it work, DON'T modify other code
fn mixup
}
fn main() {
let p1 = Point { x: 5, y: 10 };
let p2 = Point { x: "Hello", y: '中'};
let p3 = p1.mixup(p2);
assert_eq!(p3.x, 5);
assert_eq!(p3.y, '中');
println!("Success!")
}
```
7. 🌟🌟
```rust,editable
// fix the errors to make the code work
struct Point<T> {
x: T,
y: T,
}
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let p = Point{x: 5, y: 10};
println!("{}",p.distance_from_origin())
}
```
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it

View File

@@ -0,0 +1,6 @@
# Generics and Traits
Learning resources:
- English: [Rust Book 10.1, 10.2](https://doc.rust-lang.org/book/ch10-00-generics.html)
- 简体中文: [Rust语言圣经 - 模式匹配](https://course.rs/basic/trait/intro.html)

View File

@@ -0,0 +1,229 @@
# Trait Object
In [traits chapter](https://practice.rs/generics-traits/traits.html#returning-types-that-implement-traits) we have seen that we can't use `impl Trait` when returning multiple types.
Also one limitation of arrays is that they can only store elements of one type, yeah, enum is a not bad solution when our items are a fixed set of types in compile time, but trait object are more flexible and powerful here.
## Returning Traits with dyn
The Rust compiler needs to know how much space a function's return type requires. Because the different implementations of a trait probably will need different amounts of memoery, this means function need to return a concrete type or the same type when using `impl Trait`, or it can return a trait object with `dyn`.
1. 🌟🌟🌟
```rust,editable
trait Bird {
fn quack(&self) -> String;
}
struct Duck;
impl Duck {
fn swim(&self) {
println!("Look, the duck is swimming")
}
}
struct Swan;
impl Swan {
fn fly(&self) {
println!("Look, the duck.. oh sorry, the swan is flying")
}
}
impl Bird for Duck {
fn quack(&self) -> String{
"duck duck".to_string()
}
}
impl Bird for Swan {
fn quack(&self) -> String{
"swan swan".to_string()
}
}
fn main() {
// FILL in the blank
let duck = __;
duck.swim();
let bird = hatch_a_bird(2);
// this bird has forgotten how to swim, so below line will cause an error
// bird.swim();
// but it can quak
assert_eq!(bird.quack(), "duck duck");
let bird = hatch_a_bird(1);
// this bird has forgotten how to fly, so below line will cause an error
// bird.fly();
// but it can quak too
assert_eq!(bird.quack(), "swan swan");
println!("Success!")
}
// IMPLEMENT this function
fn hatch_a_bird...
```
## Array with trait objects
2. 🌟🌟
```rust,editable
trait Bird {
fn quack(&self);
}
struct Duck;
impl Duck {
fn fly(&self) {
println!("Look, the duck is flying")
}
}
struct Swan;
impl Swan {
fn fly(&self) {
println!("Look, the duck.. oh sorry, the swan is flying")
}
}
impl Bird for Duck {
fn quack(&self) {
println!("{}", "duck duck");
}
}
impl Bird for Swan {
fn quack(&self) {
println!("{}", "swan swan");
}
}
fn main() {
// FILL in the blank to make the code work
let birds __;
for bird in birds {
bird.quack();
// when duck and swan turns into Bird, they all forgot how to fly, only remeber how to quack
// so, the below code will cause an error
// bird.fly();
}
}
```
## `&dyn` and `Box<dyn>`
3. 🌟🌟
```rust,editable
// FILL in the blanks
trait Draw {
fn draw(&self) -> String;
}
impl Draw for u8 {
fn draw(&self) -> String {
format!("u8: {}", *self)
}
}
impl Draw for f64 {
fn draw(&self) -> String {
format!("f64: {}", *self)
}
}
fn main() {
let x = 1.1f64;
let y = 8u8;
// draw x
draw_with_box(__);
// draw y
draw_with_ref(&y);
println!("Success!")
}
fn draw_with_box(x: Box<dyn Draw>) {
x.draw();
}
fn draw_with_ref(x: __) {
x.draw();
}
```
## Static and Dynamic dispatch
when we use trait bounds on generics: the compiler generates nongeneric implementations of functions and methods for each concrete type that we use in place of a generic type parameter. The code that results from monomorphization is doing static dispatch, which is when the compiler knows what method youre calling at compile time.
When we use trait objects, Rust must use dynamic dispatch. The compiler doesnt know all the types that might be used with the code that is using trait objects, so it doesnt know which method implemented on which type to call. Instead, at runtime, Rust uses the pointers inside the trait object to know which method to call. There is a runtime cost when this lookup happens that doesnt occur with static dispatch. Dynamic dispatch also prevents the compiler from choosing to inline a methods code, which in turn prevents some optimizations.
However, we did get extra flexibility when using dynamic dispatch.
4. 🌟🌟
```rust,editable
trait Foo {
fn method(&self) -> String;
}
impl Foo for u8 {
fn method(&self) -> String { format!("u8: {}", *self) }
}
impl Foo for String {
fn method(&self) -> String { format!("string: {}", *self) }
}
// IMPLEMENT below with generics
fn static_dispatch...
// implement below with trait objects
fn dynamic_dispatch...
fn main() {
let x = 5u8;
let y = "Hello".to_string();
static_dispatch(x);
dynamic_dispatch(&y);
println!("Success!")
}
```
## Object safe
You can only make object-safe traits into trait objects. A trait is object safe if all the methods defined in the trait have the following properties:
- The return type isnt `Self`.
- There are no generic type parameters.
5. 🌟🌟🌟🌟
```rust,editable
// Use at least two approaches to make it work
// DON'T add/remove any code line
trait MyTrait {
fn f(&self) -> Self;
}
impl MyTrait for u32 {
fn f(&self) -> Self { 42 }
}
impl MyTrait for String {
fn f(&self) -> Self { self.clone() }
}
fn my_function(x: Box<dyn MyTrait>) {
x.f()
}
fn main() {
my_function(Box::new(13_u32));
my_function(Box::new(String::from("abc")));
println!("Success!")
}
```
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it :)

View File

@@ -0,0 +1,472 @@
# Traits
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.
> Note: Traits are similar to interfaces in other languages, although with some differences.
## Examples
```rust,editable
struct Sheep { naked: bool, name: String }
trait Animal {
// Associated function signature; `Self` refers to the implementor type.
fn new(name: String) -> Self;
// Method signatures; these will return a string.
fn name(&self) -> String;
fn noise(&self) -> String;
// 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: String) -> Sheep {
Sheep { name: name, naked: false }
}
fn name(&self) -> String {
self.name.clone()
}
fn noise(&self) -> String {
if self.is_naked() {
"baaaaah?".to_string()
} else {
"baaaaah!".to_string()
}
}
// 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".to_string());
// TODO ^ Try removing the type annotations.
dolly.talk();
dolly.shear();
dolly.talk();
}
```
## Exercises
1. 🌟🌟
```rust,editable
// fill in the two impl blocks to make the code work
// DON'T modify the code in `main`
trait Hello {
fn say_hi(&self) -> String {
String::from("hi")
}
fn say_something(&self) -> String;
}
struct Student {}
impl Hello for Student {
}
struct Teacher {}
impl Hello for Teacher {
}
fn main() {
let s = Student {};
assert_eq!(s.say_hi(), "hi");
assert_eq!(s.say_something(), "I'm a good student");
let t = Teacher {};
assert_eq!(t.say_hi(), "Hi, I'm your new teacher");
assert_eq!(t.say_something(), "I'm not a bad teacher");
println!("Success!")
}
```
### Derive
The compiler is capable of providing basic implementations for some traits via
the `#[derive]` attribute. For more info, please visit [here](https://doc.rust-lang.org/book/appendix-03-derivable-traits.html).
2. 🌟🌟
```rust,editable
// `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)
}
}
// ADD some attributes to make the code work!
// DON'T modify other codes!
struct Seconds(i32);
fn main() {
let _one_second = Seconds(1);
println!("One second looks like: {:?}", _one_second);
let _this_is_true = (_one_second == _one_second);
let _this_is_true = (_one_second > _one_second);
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);
}
```
### Operator
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.
3. 🌟🌟
```rust,editable
use std::ops;
// implement fn multiply to make the code work
// As mentiond above, `+` needs `T` to implement `std::ops::Add` Trait
// so, what about `*` ? You can find the answer here: https://doc.rust-lang.org/core/ops/
fn multipl
fn main() {
assert_eq!(6, multiply(2u8, 3u8));
assert_eq!(5.0, multiply(1.0, 5.0));
println!("Success!")
}
```
4. 🌟🌟🌟
```rust,editable
// fix the errors, DON'T modify the code in `main`
use std::ops;
struct Foo;
struct Bar;
struct FooBar;
struct BarFoo;
// The `std::ops::Add` trait is used to specify the functionality of `+`.
// Here, we make `Add<Bar>` - the trait for addition with a RHS of type `Bar`.
// The following block implements the operation: Foo + Bar = FooBar
impl ops::Add<Bar> for Foo {
type Output = FooBar;
fn add(self, _rhs: Bar) -> FooBar {
FooBar
}
}
impl ops::Sub<Foo> for Bar {
type Output = BarFoo;
fn sub(self, _rhs: Foo) -> BarFoo {
BarFoo
}
}
fn main() {
// DON'T modify the below code
// you need to derive some trait for FooBar to make it comparable
assert_eq!(Foo + Bar, FooBar);
assert_eq!(Foo - Bar, BarFoo);
println!("Success!")
}
```
### Use trait as function parameters
Instead of a concrete type for the item parameter, we specify the impl keyword and the trait name. This parameter accepts any type that implements the specified trait.
5. 🌟🌟🌟
```rust,editable
// implement `fn summary` to make the code work
// fix the errors without removing any code line
trait Summary {
fn summarize(&self) -> String;
}
#[derive(Debug)]
struct Post {
title: String,
author: String,
content: String,
}
impl Summary for Post {
fn summarize(&self) -> String {
format!("The author of post {} is {}", self.title, self.author)
}
}
#[derive(Debug)]
struct Weibo {
username: String,
content: String,
}
impl Summary for Weibo {
fn summarize(&self) -> String {
format!("{} published a weibo {}", self.username, self.content)
}
}
fn main() {
let post = Post {
title: "Popular Rust".to_string(),
author: "Sunface".to_string(),
content: "Rust is awesome!".to_string(),
};
let weibo = Weibo {
username: "sunface".to_string(),
content: "Weibo seems to be worse than Tweet".to_string(),
};
summary(post);
summary(weibo);
println!("{:?}", post);
println!("{:?}", weibo);
}
// implement `fn summary` below
```
### Returning Types that Implement Traits
We can also use the impl Trait syntax in the return position to return a value of some type that implements a trait.
However, you can only use impl Trait if youre returning a single type, using Trait Objects instead when you really need to return serveral types.
6. 🌟🌟
```rust,editable
struct Sheep {}
struct Cow {}
trait Animal {
fn noise(&self) -> String;
}
impl Animal for Sheep {
fn noise(&self) -> String {
"baaaaah!".to_string()
}
}
impl Animal for Cow {
fn noise(&self) -> String {
"moooooo!".to_string()
}
}
// Returns some struct that implements Animal, but we don't know which one at compile time.
// FIX the erros here, you can make a fake random, or you can use trait object
fn random_animal(random_number: f64) -> impl Animal {
if random_number < 0.5 {
Sheep {}
} else {
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());
}
```
### Trait bound
The `impl Trait` syntax works for straightforward cases but is actually syntax sugar for a longer form, which is called a trait bound.
When working with generics, the type parameters often must use traits as bounds to stipulate what functionality a type implements.
7. 🌟🌟
```rust, editable
fn main() {
assert_eq!(sum(1, 2), 3);
}
// implement `fn sum` with trait bound in two ways
fn sum<T>(x: T, y: T) -> T {
x + y
}
```
8. 🌟🌟
```rust,editable
// FIX the errors
struct Pair<T> {
x: T,
y: T,
}
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self {
x,
y,
}
}
}
impl<T: std::fmt::Debug + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("The largest member is x = {:?}", self.x);
} else {
println!("The largest member is y = {:?}", self.y);
}
}
}
struct Unit(i32);
fn main() {
let pair = Pair{
x: Unit(1),
y: Unit(3)
};
pair.cmp_display();
}
```
9. 🌟🌟🌟
```rust,editable
// fill in the blanks to make it work
fn example1() {
// `T: Trait` is the commonly used way
// `T: Fn(u32) -> u32` specifies that we can only pass a closure to `T`
struct Cacher<T: Fn(u32) -> u32> {
calculation: T,
value: Option<u32>,
}
impl<T: Fn(u32) -> u32> Cacher<T> {
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: None,
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
},
}
}
}
let mut cacher = Cacher::new(|x| x+1);
assert_eq!(cacher.value(10), __);
assert_eq!(cacher.value(15), __);
}
fn example2() {
// We can also use `where` to constrain `T`
struct Cacher<T>
where T: Fn(u32) -> u32,
{
calculation: T,
value: Option<u32>,
}
impl<T> Cacher<T>
where T: Fn(u32) -> u32,
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: None,
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
},
}
}
}
let mut cacher = Cacher::new(|x| x+1);
assert_eq!(cacher.value(20), __);
assert_eq!(cacher.value(25), __);
}
fn main() {
example1();
example2();
println!("Success!")
}
```
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it :)