commit
908a3bb0c0
|
@ -9,7 +9,7 @@ pub trait CacheableItem: Clone + Default + fmt::Debug + Decodable + Encodable {
|
|||
}
|
||||
```
|
||||
|
||||
Using of `Address` is much more clearable and convenient than `AsRef<[u8]> + Clone + fmt::Debug + Eq + Hash`.
|
||||
Using of `Address` is much more clearer and convenient than `AsRef<[u8]> + Clone + fmt::Debug + Eq + Hash`.
|
||||
|
||||
1. πππ
|
||||
```rust,editable
|
||||
|
@ -89,7 +89,7 @@ fn main() {
|
|||
assert_eq!(Point { x: 2, y: 3 } - Point { x: 1, y: 0 },
|
||||
Point { x: 1, y: 3 });
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -110,7 +110,7 @@ trait AgeWidget {
|
|||
fn get(&self) -> u8;
|
||||
}
|
||||
|
||||
// A form with both a UsernameWidget and an AgeWidget
|
||||
// A form with both a UsernameWidget and an AgeWidget.
|
||||
struct Form {
|
||||
username: String,
|
||||
age: u8,
|
||||
|
@ -141,10 +141,10 @@ fn main() {
|
|||
|
||||
let username = UsernameWidget::get(&form);
|
||||
assert_eq!("rustacean".to_owned(), username);
|
||||
let age = AgeWidget::get(&form); // you can also use `<Form as AgeWidget>::get`
|
||||
let age = AgeWidget::get(&form); // You can also use `<Form as AgeWidget>::get`
|
||||
assert_eq!(28, age);
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -187,7 +187,7 @@ fn main() {
|
|||
|
||||
assert_eq!(__, "*waving arms furiously*");
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -253,7 +253,7 @@ fn main() {
|
|||
## Orphan Rules
|
||||
We canβt implement external traits on external types. For example, we canβt implement the `Display` trait on `Vec<T>` within our own crate, because `Display` and `Vec<T>` are defined in the standard library and arenβt 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 peopleβs code canβt break your code and vice versa.
|
||||
This restriction is often called the orphan rule, so named because the parent type is not present. This rule ensures that other peopleβs code canβt break your code and vice versa.
|
||||
|
||||
Itβs possible to get around this restriction using the newtype pattern, which involves creating a new type in a tuple struct.
|
||||
|
||||
|
|
|
@ -25,15 +25,15 @@ impl<T: Debug, const N: usize> Debug for ArrayPair<T, N> {
|
|||
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>(); // Okay: `M` is a const parameter
|
||||
foo::<2021>(); // Okay: `2021` is a literal
|
||||
foo::<{20 * 100 + 20 * 10 + 1}>(); // Okay: 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`
|
||||
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`
|
||||
let _: [u8; M]; // Okay: `M` is a const parameter
|
||||
let _: [u8; std::mem::size_of::<T>()]; // Error: const expression contains the generic parameter `T`
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -89,14 +89,14 @@ fn main() {
|
|||
}
|
||||
];
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
2. ππ
|
||||
```rust,editable
|
||||
|
||||
// fill in the blanks to make it work
|
||||
// Fill in the blanks to make it work.
|
||||
fn print_array<__>(__) {
|
||||
println!("{:?}", arr);
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
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.
|
||||
3. πππ Sometimes we want to limit the size of a variable, e.g when using in embedding environments, then `const expressions` will fit your needs.
|
||||
|
||||
```rust,editable
|
||||
#![allow(incomplete_features)]
|
||||
|
@ -122,15 +122,15 @@ where
|
|||
//...
|
||||
}
|
||||
|
||||
// fix the errors in main
|
||||
// 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 ?
|
||||
check_size(["helloδ½ ε₯½"; __]); // Size of &str ?
|
||||
check_size(["helloδ½ ε₯½".to_string(); __]); // Size of String?
|
||||
check_size(['δΈ'; __]); // Size of char ?
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
1. πππ
|
||||
```rust,editable
|
||||
|
||||
// fill in the blanks to make it work
|
||||
// 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`.
|
||||
|
@ -29,14 +29,14 @@ fn main() {
|
|||
// Implicitly specified type parameter `char` to `generic()`.
|
||||
generic(__);
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
2. ππ A function call with explicitly specified type parameters looks like: `fun::<A, B, ...>()`.
|
||||
```rust,editable
|
||||
|
||||
// implement the generic function below
|
||||
// Implement the generic function below.
|
||||
fn sum
|
||||
|
||||
fn main() {
|
||||
|
@ -44,7 +44,7 @@ fn main() {
|
|||
assert_eq!(50, sum(20, 30));
|
||||
assert_eq!(2.46, sum(1.23, 1.23));
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -54,38 +54,38 @@ fn main() {
|
|||
3. π
|
||||
```rust,editable
|
||||
|
||||
// implement struct Point to make it work
|
||||
// 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!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
4. ππ
|
||||
```rust,editable
|
||||
|
||||
// modify this struct to make the code work
|
||||
// Modify this struct to make the code work
|
||||
struct Point<T> {
|
||||
x: T,
|
||||
y: T,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// DON'T modify here
|
||||
// DON'T modify this code.
|
||||
let p = Point{x: 5, y : "hello".to_string()};
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
5. ππ
|
||||
```rust,editable
|
||||
|
||||
// add generic for Val to make the code work, DON'T modify the code in `main`
|
||||
// Add generic for Val to make the code work, DON'T modify the code in `main`.
|
||||
struct Val {
|
||||
val: f64,
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ struct Point<T, U> {
|
|||
}
|
||||
|
||||
impl<T, U> Point<T, U> {
|
||||
// implement mixup to make it work, DON'T modify other code
|
||||
// Implement mixup to make it work, DON'T modify other code.
|
||||
fn mixup
|
||||
}
|
||||
|
||||
|
@ -127,14 +127,14 @@ fn main() {
|
|||
assert_eq!(p3.x, 5);
|
||||
assert_eq!(p3.y, 'δΈ');
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
7. ππ
|
||||
```rust,editable
|
||||
|
||||
// fix the errors to make the code work
|
||||
// Fix the errors to make the code work.
|
||||
struct Point<T> {
|
||||
x: T,
|
||||
y: T,
|
||||
|
@ -148,7 +148,7 @@ impl Point<f32> {
|
|||
|
||||
fn main() {
|
||||
let p = Point{x: 5, y: 10};
|
||||
println!("{}",p.distance_from_origin())
|
||||
println!("{}",p.distance_from_origin());
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ In [traits chapter](https://practice.rs/generics-traits/traits.html#returning-ty
|
|||
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`.
|
||||
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 memory, 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
|
||||
|
@ -39,26 +39,26 @@ impl Bird for Swan {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
// FILL in the blank
|
||||
// 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
|
||||
// This bird has forgotten how to swim, so below line will cause an error.
|
||||
// bird.swim();
|
||||
// but it can quak
|
||||
// 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
|
||||
// This bird has forgotten how to fly, so below line will cause an error.
|
||||
// bird.fly();
|
||||
// but it can quak too
|
||||
// But it can quak too.
|
||||
assert_eq!(bird.quack(), "swan swan");
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
|
||||
// IMPLEMENT this function
|
||||
// IMPLEMENT this function.
|
||||
fn hatch_a_bird...
|
||||
|
||||
```
|
||||
|
@ -95,13 +95,13 @@ impl Bird for Swan {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
// FILL in the blank to make the code work
|
||||
// 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
|
||||
// When duck and swan turn into Birds, they all forgot how to fly, only remember how to quack.
|
||||
// So, the code below will cause an error.
|
||||
// bird.fly();
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ fn main() {
|
|||
3. ππ
|
||||
```rust,editable
|
||||
|
||||
// FILL in the blanks
|
||||
// FILL in the blanks.
|
||||
trait Draw {
|
||||
fn draw(&self) -> String;
|
||||
}
|
||||
|
@ -134,13 +134,13 @@ fn main() {
|
|||
let x = 1.1f64;
|
||||
let y = 8u8;
|
||||
|
||||
// draw x
|
||||
// Draw x.
|
||||
draw_with_box(__);
|
||||
|
||||
// draw y
|
||||
// Draw y.
|
||||
draw_with_ref(&y);
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
|
||||
fn draw_with_box(x: Box<dyn Draw>) {
|
||||
|
@ -153,11 +153,11 @@ fn draw_with_ref(x: __) {
|
|||
```
|
||||
|
||||
## 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 youβre calling at compile time.
|
||||
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 youβre calling at compile time.
|
||||
|
||||
When we use trait objects, Rust must use dynamic dispatch. The compiler doesnβt know all the types that might be used with the code that is using trait objects, so it doesnβt 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 doesnβt occur with static dispatch. Dynamic dispatch also prevents the compiler from choosing to inline a methodβs code, which in turn prevents some optimizations.
|
||||
|
||||
However, we did get extra flexibility when using dynamic dispatch.
|
||||
However, we do get extra flexibility when using dynamic dispatch.
|
||||
|
||||
4. ππ
|
||||
```rust,editable
|
||||
|
@ -174,10 +174,10 @@ impl Foo for String {
|
|||
fn method(&self) -> String { format!("string: {}", *self) }
|
||||
}
|
||||
|
||||
// IMPLEMENT below with generics
|
||||
// IMPLEMENT below with generics.
|
||||
fn static_dispatch...
|
||||
|
||||
// implement below with trait objects
|
||||
// Implement below with trait objects.
|
||||
fn dynamic_dispatch...
|
||||
|
||||
fn main() {
|
||||
|
@ -187,7 +187,7 @@ fn main() {
|
|||
static_dispatch(x);
|
||||
dynamic_dispatch(&y);
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -200,8 +200,8 @@ You can only make object-safe traits into trait objects. A trait is object safe
|
|||
5. ππππ
|
||||
```rust,editable
|
||||
|
||||
// Use at least two approaches to make it work
|
||||
// DON'T add/remove any code line
|
||||
// Use at least two approaches to make it work.
|
||||
// DON'T add/remove any code line.
|
||||
trait MyTrait {
|
||||
fn f(&self) -> Self;
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ fn main() {
|
|||
my_function(Box::new(13_u32));
|
||||
my_function(Box::new(String::from("abc")));
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -81,8 +81,8 @@ fn main() {
|
|||
1. ππ
|
||||
```rust,editable
|
||||
|
||||
// fill in the two impl blocks to make the code work
|
||||
// DON'T modify the code in `main`
|
||||
// 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")
|
||||
|
@ -107,7 +107,7 @@ fn main() {
|
|||
assert_eq!(t.say_hi(), "Hi, I'm your new teacher");
|
||||
assert_eq!(t.say_something(), "I'm not a bad teacher");
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -135,7 +135,7 @@ impl Inches {
|
|||
}
|
||||
|
||||
// ADD some attributes to make the code work!
|
||||
// DON'T modify other codes!
|
||||
// DON'T modify other code!
|
||||
struct Seconds(i32);
|
||||
|
||||
fn main() {
|
||||
|
@ -171,23 +171,23 @@ In Rust, many of the operators can be overloaded via traits. That is, some opera
|
|||
|
||||
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/
|
||||
// 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!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
4. πππ
|
||||
```rust,editable
|
||||
|
||||
// fix the errors, DON'T modify the code in `main`
|
||||
// Fix the errors, DON'T modify the code in `main`.
|
||||
use std::ops;
|
||||
|
||||
struct Foo;
|
||||
|
@ -217,12 +217,12 @@ impl ops::Sub<Foo> for Bar {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
// DON'T modify the below code
|
||||
// you need to derive some trait for FooBar to make it comparable
|
||||
// DON'T modify the code below.
|
||||
// You need to derive some trait for FooBar to make it comparable.
|
||||
assert_eq!(Foo + Bar, FooBar);
|
||||
assert_eq!(Foo - Bar, BarFoo);
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -232,8 +232,8 @@ Instead of a concrete type for the item parameter, we specify the impl keyword a
|
|||
5. πππ
|
||||
```rust,editable
|
||||
|
||||
// implement `fn summary` to make the code work
|
||||
// fix the errors without removing any code line
|
||||
// Implement `fn summary` to make the code work.
|
||||
// Fix the errors without removing any code line
|
||||
trait Summary {
|
||||
fn summarize(&self) -> String;
|
||||
}
|
||||
|
@ -281,14 +281,14 @@ fn main() {
|
|||
println!("{:?}", weibo);
|
||||
}
|
||||
|
||||
// implement `fn summary` below
|
||||
// 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.
|
||||
However, you can only use impl Trait if youβre returning a single type, use Trait Objects instead when you really need to return several types.
|
||||
|
||||
6. ππ
|
||||
```rust,editable
|
||||
|
@ -313,7 +313,7 @@ impl Animal for Cow {
|
|||
}
|
||||
|
||||
// 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
|
||||
// FIX the errors 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 {}
|
||||
|
@ -340,7 +340,7 @@ fn main() {
|
|||
assert_eq!(sum(1, 2), 3);
|
||||
}
|
||||
|
||||
// implement `fn sum` with trait bound in two ways
|
||||
// Implement `fn sum` with trait bound in two ways.
|
||||
fn sum<T>(x: T, y: T) -> T {
|
||||
x + y
|
||||
}
|
||||
|
@ -348,7 +348,7 @@ fn sum<T>(x: T, y: T) -> T {
|
|||
8. ππ
|
||||
```rust,editable
|
||||
|
||||
// FIX the errors
|
||||
// FIX the errors.
|
||||
struct Pair<T> {
|
||||
x: T,
|
||||
y: T,
|
||||
|
@ -388,10 +388,10 @@ fn main() {
|
|||
9. πππ
|
||||
```rust,editable
|
||||
|
||||
// fill in the blanks to make it work
|
||||
// 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`
|
||||
// `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>,
|
||||
|
@ -424,7 +424,7 @@ fn example1() {
|
|||
|
||||
|
||||
fn example2() {
|
||||
// We can also use `where` to constrain `T`
|
||||
// We can also use `where` to construct `T`
|
||||
struct Cacher<T>
|
||||
where T: Fn(u32) -> u32,
|
||||
{
|
||||
|
@ -465,7 +465,7 @@ fn main() {
|
|||
example1();
|
||||
example2();
|
||||
|
||||
println!("Success!")
|
||||
println!("Success!");
|
||||
}
|
||||
```
|
||||
|
||||
|
|
Loadingβ¦
Reference in New Issue