diff --git a/ChangeLog.md b/ChangeLog.md index 087322d..319a867 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,8 @@ ## 2022-03-31 - Add [Iterator](https://practice.rs/functional-programing/iterator.html) +- Add [Newtype and DST](https://practice.rs/newtype-sized.html) + ## 2022-03-30 diff --git a/en/src/SUMMARY.md b/en/src/SUMMARY.md index 9921720..61fb5f6 100644 --- a/en/src/SUMMARY.md +++ b/en/src/SUMMARY.md @@ -56,7 +56,7 @@ - [Functional programing](functional-programing/intro.md) - [Closure](functional-programing/cloure.md) - [Iterator](functional-programing/iterator.md) -- [newtype and Sized TODO](newtype-sized.md) +- [newtype and DST](newtype-sized.md) - [Smart pointers TODO](smart-pointers/intro.md) - [Box](smart-pointers/box.md) - [Deref](smart-pointers/deref.md) diff --git a/en/src/newtype-sized.md b/en/src/newtype-sized.md index fc3f074..4f0ccb7 100644 --- a/en/src/newtype-sized.md +++ b/en/src/newtype-sized.md @@ -1 +1,204 @@ # newtype and Sized + +## Newtype +The orphan rule tells us that we are allowed to implement a trait on a type as long as either the trait or the type are local to our crate. + +The **newtype pattern** can help us get around this restriction, which involves creating a new type in a **tuple struct**. + +1γ€πŸŒŸ +```rust,editable +use std::fmt; + +/* Define the Wrapper type */ +__; + +// Display is an external trait +impl fmt::Display for Wrapper { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "[{}]", self.0.join(", ")) + } +} + +fn main() { + // Vec is an external type, so you cannot implement Display trait on Vec type + let w = Wrapper(vec![String::from("hello"), String::from("world")]); + println!("w = {}", w); +} +``` + +2γ€πŸŒŸ Hide the methods of the orginal type +```rust,editable +/* Make it workd */ +struct Meters(u32); + +fn main() { + let i: u32 = 2; + assert_eq!(i.pow(2), 4); + + let n = Meters(i); + // The `pow` method is defined on `u32` type, we can't directly call it + assert_eq!(n.pow(2), 4); +} +``` + +3γ€πŸŒŸπŸŒŸ The `newtype` idiom gives compile time guarantees that the right type of value is suplied to a program. +```rust,editable +/* Make it work */ +struct Years(i64); + +struct Days(i64); + +impl Years { + pub fn to_days(&self) -> Days { + Days(self.0 * 365) + } +} + + +impl Days { + pub fn to_years(&self) -> Years { + Years(self.0 / 365) + } +} + +// an age verification function that checks age in years, must be given a value of type Years. +fn old_enough(age: &Years) -> bool { + age.0 >= 18 +} + +fn main() { + let age = Years(5); + let age_days = age.to_days(); + println!("Old enough {}", old_enough(&age)); + println!("Old enough {}", old_enough(&age_days)); +} +``` + +4γ€πŸŒŸπŸŒŸ +```rust,editable +use std::ops::Add; +use std::fmt::{self, format}; + +struct Meters(u32); +impl fmt::Display for Meters { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "There are still {} meters left", self.0) + } +} + +impl Add for Meters { + type Output = Self; + + fn add(self, other: Meters) -> Self { + Self(self.0 + other.0) + } +} +fn main() { + let d = calculate_distance(Meters(10), Meters(20)); + assert_eq!(format!("{}",d), "There are still 30 meters left"); +} + +/* implement calculate_distance */ +fn calculate_distance +``` + +## Type alias +The most importance of type alias is to improve the readability of our codes. + +```rust +type Thunk = Box; + +let f: Thunk = Box::new(|| println!("hi")); + +fn takes_long_type(f: Thunk) { + // --snip-- +} + +fn returns_long_type() -> Thunk { + // --snip-- +} +``` + +```rust +type Result = std::result::Result; +``` + +And Unlike newtype, type alias don't create new types, so the following code is valid: +```rust +type Meters = u32; + +let x: u32 = 5; +let y: Meters = 5; + +println!("x + y = {}", x + y); +``` + +5γ€πŸŒŸ +```rust,editable +enum VeryVerboseEnumOfThingsToDoWithNumbers { + Add, + Subtract, +} + +/* Fill in the blank */ +__ + +fn main() { + // We can refer to each variant via its alias, not its long and inconvenient + // name. + let x = Operations::Add; +} +``` + +6γ€πŸŒŸπŸŒŸ There are a few preserved alias in Rust, one of which can be used in `impl` blocks. +```rust,editable +enum VeryVerboseEnumOfThingsToDoWithNumbers { + Add, + Subtract, +} + +impl VeryVerboseEnumOfThingsToDoWithNumbers { + fn run(&self, x: i32, y: i32) -> i32 { + match self { + __::Add => x + y, + __::Subtract => x - y, + } + } +} +``` + +## DST and unsized type +These concepts are complicated, so we are not going to explain here, but you can find them in [The Book](https://doc.rust-lang.org/book/ch19-04-advanced-types.html?highlight=DST#dynamically-sized-types-and-the-sized-trait). + +7γ€πŸŒŸπŸŒŸπŸŒŸ Array with dynamic length is a Dynamic Sized Type ( DST ), we can't directly use it +```rust,editable +/* Make it work with const generics */ +fn my_function(n: usize) -> [u32; usize] { + [123; n] +} + +fn main() { + let arr = my_function(); + println!("{:?}",arr); +} +``` + +8γ€πŸŒŸπŸŒŸ Slice is unsized type, but the reference of slice is not. +```rust,editable +/* Make it work with slice references */ +fn main() { + let s: str = "Hello there!"; + + let arr: [u8] = [1, 2, 3]; +} +``` + +9γ€πŸŒŸπŸŒŸ Trait is also a unsized type +```rust,editable +/* Make it work in two ways */ +use std::fmt::Display; +fn foobar(thing: Display) {} + +fn main() { +} +``` \ No newline at end of file diff --git a/solutions/newtype-sized.md b/solutions/newtype-sized.md new file mode 100644 index 0000000..c3a0889 --- /dev/null +++ b/solutions/newtype-sized.md @@ -0,0 +1,162 @@ +1、 +```rust +use std::fmt; + +struct Wrapper(Vec); + +impl fmt::Display for Wrapper { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "[{}]", self.0.join(", ")) + } +} + +fn main() { + let w = Wrapper(vec![String::from("hello"), String::from("world")]); + println!("w = {}", w); +} +``` + +2、 +```rust +struct Meters(u32); + +fn main() { + let i: u32 = 2; + assert_eq!(i.pow(2), 4); + + let n = Meters(i); + assert_eq!(n.0.pow(2), 4); +} +``` + +3、 +```rust +struct Years(i64); + +struct Days(i64); + +impl Years { + pub fn to_days(&self) -> Days { + Days(self.0 * 365) + } +} + + +impl Days { + pub fn to_years(&self) -> Years { + Years(self.0 / 365) + } +} + +// an age verification function that checks age in years, must be given a value of type Years. +fn old_enough(age: &Years) -> bool { + age.0 >= 18 +} + +fn main() { + let age = Years(5); + let age_days = age.to_days(); + println!("Old enough {}", old_enough(&age)); + println!("Old enough {}", old_enough(&age_days.to_years())); +} +``` + +4、Sometimes `newtype` pattern can provide extra readability. +```rust +use std::ops::Add; +use std::fmt::{self, format}; + +struct Meters(u32); +impl fmt::Display for Meters { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "There are still {} meters left", self.0) + } +} + +impl Add for Meters { + type Output = Self; + + fn add(self, other: Meters) -> Self { + Self(self.0 + other.0) + } +} +fn main() { + let d = calculate_distance(Meters(10), Meters(20)); + assert_eq!(format!("{}",d), "There are still 30 meters left"); +} + +/* implement calculate_distance */ +fn calculate_distance(d1: Meters, d2: Meters) -> Meters { + d1 + d2 +} +``` + +5、 +```rust +enum VeryVerboseEnumOfThingsToDoWithNumbers { + Add, + Subtract, +} + +/* Fill in the blank */ +type Operations = VeryVerboseEnumOfThingsToDoWithNumbers; + +fn main() { + // We can refer to each variant via its alias, not its long and inconvenient + // name. + let x = Operations::Add; +} +``` + +6、 +```rust +enum VeryVerboseEnumOfThingsToDoWithNumbers { + Add, + Subtract, +} + +impl VeryVerboseEnumOfThingsToDoWithNumbers { + fn run(&self, x: i32, y: i32) -> i32 { + match self { + Self::Add => x + y, + Self::Subtract => x - y, + } + } +} + +fn main() { + +} +``` + +7、 +```rust +fn my_function() -> [u32; N] { + [123; N] +} + +fn main() { + let arr = my_function::<5>(); + println!("{:?}",arr); +} +``` + +8、 +```rust +fn main() { + let s: &str = "Hello there!"; + + let arr: &[u8] = &[1, 2, 3]; +} +``` + + +9、 +```rust +use std::fmt::Display; +fn foobar_1(thing: &dyn Display) {} +fn foobar_2(thing: Box) {} + +fn main() { +} +``` \ No newline at end of file