diff --git a/Readme.md b/Readme.md index 2e1a33b..9e97f37 100644 --- a/Readme.md +++ b/Readme.md @@ -6,10 +6,15 @@ This book was designed for easily diving into Rust๏ผŒand it's very easy to use: ## Features - Read, Edit and Run the exercise ONLINE + - There are three parts in each chapter: examples, exercises and practices + - Covering nearly all aspects of Rust, such as **async/await, threads, sync primitives, optimizing and stand libraries** etc + - Solution for each exercise + - Difficulty from easy to super hard: easy ๐ŸŒŸ medium ๐ŸŒŸ๐ŸŒŸ hard ๐ŸŒŸ๐ŸŒŸ๐ŸŒŸ super hard ๐ŸŒŸ๐ŸŒŸ๐ŸŒŸ๐ŸŒŸ + - Both [English](https://practice.rs) and [Chinsese](https://zh.practice.rs) are supported diff --git a/solutions/generics-traits/traits.md b/solutions/generics-traits/traits.md index 6989dc4..92b96c1 100644 --- a/solutions/generics-traits/traits.md +++ b/solutions/generics-traits/traits.md @@ -1,7 +1,417 @@ 1. ```rust +trait Hello { + fn say_hi(&self) -> String { + String::from("hi") + } + + fn say_something(&self) -> String; +} + +struct Student {} +impl Hello for Student { + fn say_something(&self) -> String { + String::from("I'm a good student") + } +} +struct Teacher {} +impl Hello for Teacher { + fn say_hi(&self) -> String { + String::from("Hi, I'm your new teacher") + } + + fn say_something(&self) -> String { + String::from("I'm not a bad 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"); +} ``` -1. +2. ```rust +// `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 +#[derive(Debug,PartialEq,PartialOrd)] +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); +} +``` + +3. +```rust +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 multiply>(x: T, y: T) -> T { + x * y +} + +fn main() { + assert_eq!(6, multiply(2u8, 3u8)); + assert_eq!(5.0, multiply(1.0, 5.0)); +} +``` + +4. +```rust +use std::ops; + +struct Foo; +struct Bar; + +#[derive(PartialEq, Debug)] +struct FooBar; + +#[derive(PartialEq, 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 { + FooBar + } +} + +impl ops::Sub for Foo { + type Output = BarFoo; + + fn sub(self, _rhs: Bar) -> 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); +} +``` + +5. +```rust +// 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); +} + +fn summary(t: &impl Summary) { + let _ = t.summarize(); +} +``` + +6. +```rust +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 { + Sheep {} + } +} + +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()); +} +``` + +```rust +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) -> 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()); +} +``` + +7. +```rust +fn main() { + assert_eq!(sum(1, 2), 3); + assert_eq!(sum(1.0, 2.0), 3.0); +} + +fn sum>(x: T, y: T) -> T { + x + y +} +``` + +```rust +fn main() { + assert_eq!(sum(1, 2), 3); + assert_eq!(sum(1.0, 2.0), 3.0); +} + +fn sum(x: T, y: T) -> T +where + T: std::ops::Add, +{ + x + y +} +``` + +8. +```rust +struct Pair { + x: T, + y: T, +} + +impl Pair { + fn new(x: T, y: T) -> Self { + Self { + x, + y, + } + } +} + +impl Pair { + 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); + } + } +} + +#[derive(Debug, PartialEq, PartialOrd)] +struct Unit(i32); + +fn main() { + let pair = Pair{ + x: Unit(1), + y: Unit(3) + }; + + pair.cmp_display(); +} +``` + +9. +```rust +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 u32> { + calculation: T, + value: Option, + } + + impl u32> Cacher { + fn new(calculation: T) -> Cacher { + 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), 11); + assert_eq!(cacher.value(15), 11); +} + + +fn example2() { + // We can also use `where` to constrain `T` + struct Cacher + where T: Fn(u32) -> u32, + { + calculation: T, + value: Option, + } + + impl Cacher + where T: Fn(u32) -> u32, + { + fn new(calculation: T) -> Cacher { + 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), 21); + assert_eq!(cacher.value(25), 21); +} + + + +fn main() { + example1(); + example2(); +} ``` \ No newline at end of file diff --git a/src/generics-traits/traits.md b/src/generics-traits/traits.md index f47281d..b074453 100644 --- a/src/generics-traits/traits.md +++ b/src/generics-traits/traits.md @@ -1,10 +1,11 @@ # 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 a feature often called interfaces in other languages, although with some differences. +> Note: Traits are similar to interfaces in other languages, although with some differences. ## Examples ```rust,editable + struct Sheep { naked: bool, name: String } trait Animal { @@ -76,8 +77,384 @@ fn main() { } ``` +## 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() { - assert_eq!(5, sum(2i8, 3u8)); - assert_eq!(50, sum(20, 30.1)); -} \ No newline at end of file + 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"); +} +``` + +### 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)); +} +``` + +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` - 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 { + FooBar + } +} + +impl ops::Sub 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); +} +``` + +### 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 youโ€™re 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>(x: T, y: T) -> T { + x + y +} +``` +8. ๐ŸŒŸ๐ŸŒŸ +```rust,editable +struct Pair { + x: T, + y: T, +} + +impl Pair { + fn new(x: T, y: T) -> Self { + Self { + x, + y, + } + } +} + +impl Pair { + 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 u32> { + calculation: T, + value: Option, + } + + impl u32> Cacher { + fn new(calculation: T) -> Cacher { + 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 + where T: Fn(u32) -> u32, + { + calculation: T, + value: Option, + } + + impl Cacher + where T: Fn(u32) -> u32, + { + fn new(calculation: T) -> Cacher { + 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(); +} +``` \ No newline at end of file diff --git a/src/why-exercise.md b/src/why-exercise.md index cf87a0f..2268925 100644 --- a/src/why-exercise.md +++ b/src/why-exercise.md @@ -4,9 +4,13 @@ This book was designed for easily diving into Rust๏ผŒand it's very easy to use: ## Features - There are three parts in each chapter: examples, exercises and practices + - Covering nearly all aspects of Rust, such as **async/await, threads, sync primitives, optimizing and stand libraries** etc + - Solution for each exercise + - Difficulty from easy to super hard: easy ๐ŸŒŸ medium ๐ŸŒŸ๐ŸŒŸ hard ๐ŸŒŸ๐ŸŒŸ๐ŸŒŸ super hard ๐ŸŒŸ๐ŸŒŸ๐ŸŒŸ๐ŸŒŸ + - Both [English](https://practice.rs) and [Chinsese](https://zh.practice.rs) are supported > Part of our examples and exercises are borrowed from [Rust By Example](https://github.com/rust-lang/rust-by-example), thanks for the great works you have been doing! diff --git a/zh-CN/src/why-exercise.md b/zh-CN/src/why-exercise.md index adda035..7f28e79 100644 --- a/zh-CN/src/why-exercise.md +++ b/zh-CN/src/why-exercise.md @@ -14,7 +14,7 @@ 2. ่ฆ†็›–้ขๆ›ดๅนฟ๏ผŒ้™คไบ† Rust ่ฏญ่จ€ๆœฌ่บซๅค–๏ผŒ่ฟ˜ๅŒ…ๆ‹ฌไบ†**asyncๅผ‚ๆญฅ็ผ–็จ‹, ๅคš็บฟ็จ‹, ๅนถๅ‘ๅŽŸ่ฏญ, ๆ€ง่ƒฝไผ˜ๅŒ–ๅ’Œๆ ‡ๅ‡†ๅบ“็ญ‰ๅ†…ๅฎนs** -3. ้€š่ฟ‡ไธ€ไบ›ๅฐๅž‹ๆ—ถ้—ด้กน็›ฎๆฅๅฎž่ทต Rust +3. ้€š่ฟ‡ไธ€ไบ›ๅฐๅž‹ๅฎž่ทต้กน็›ฎๆฅๅฎž่ทต Rust 4. ๆ”ฏๆŒ่‹ฑ่ฏญๅ’Œไธญๆ–‡