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. 🌟
1. use std::fmt;

3. /* Define the Wrapper type */
4. __;

6. // Display is an external trait
7. impl fmt::Display for Wrapper {
8. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
9. write!(f, "[{}]", self.0.join(", "))
10. }
11. }

13. fn main() {
14. // Vec is an external type, so you cannot implement Display trait on Vec type
15. let w = Wrapper(vec![String::from("hello"), String::from("world")]);
16. println!("w = {}", w);
17. }
  1. 🌟 Hide the methods of the original type.
1. /* Make it workd */
2. struct Meters(u32);

4. fn main() {
5. let i: u32 = 2;
6. assert_eq!(i.pow(2), 4);

8. let n = Meters(i);
9. // The `pow` method is defined on `u32` type, we can't directly call it
10. assert_eq!(n.pow(2), 4);
11. }
  1. 🌟🌟 The newtype idiom gives compile time guarantees that the right type of value is supplied to a program.
1. /* Make it work */
2. struct Years(i64);

4. struct Days(i64);

6. impl Years {
7. pub fn to_days(&self) -> Days {
8. Days(self.0 * 365)
9. }
10. }

12. impl Days {
13. pub fn to_years(&self) -> Years {
14. Years(self.0 / 365)
15. }
16. }

18. // An age verification function that checks age in years, must be given a value of type Years.
19. fn old_enough(age: &Years) -> bool {
20. age.0 >= 18
21. }

23. fn main() {
24. let age = Years(5);
25. let age_days = age.to_days();
26. println!("Old enough {}", old_enough(&age));
27. println!("Old enough {}", old_enough(&age_days));
28. }
  1. 🌟🌟
1. use std::ops::Add;
2. use std::fmt::{self, format};

4. struct Meters(u32);
5. impl fmt::Display for Meters {
6. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7. write!(f, "There are still {} meters left", self.0)
8. }
9. }

11. impl Add for Meters {
12. type Output = Self;

14. fn add(self, other: Meters) -> Self {
15. Self(self.0 + other.0)
16. }
17. }
18. fn main() {
19. let d = calculate_distance(Meters(10), Meters(20));
20. assert_eq!(format!("{}",d), "There are still 30 meters left");
21. }

23. /* Implement calculate_distance  */
24. fn calculate_distance

Type alias

Type alias is important to improve the readability of our code.

1. #![allow(unused)]
2. fn main() {
3. type Thunk = Box<dyn Fn() + Send + 'static>;

5. let f: Thunk = Box::new(|| println!("hi"));

7. fn takes_long_type(f: Thunk) {
8. // --snip--
9. }

11. fn returns_long_type() -> Thunk {
12. // --snip--
13. }
14. }
1. #![allow(unused)]
2. fn main() {
3. type Result<T> = std::result::Result<T, std::io::Error>;
4. }

And Unlike newtype, type alias don’t create new types, so the following code is valid:

1. #![allow(unused)]
2. fn main() {
3. type Meters = u32;

5. let x: u32 = 5;
6. let y: Meters = 5;

8. println!("x + y = {}", x + y);
9. }
  1. 🌟
1. enum VeryVerboseEnumOfThingsToDoWithNumbers {
2. Add,
3. Subtract,
4. }

6. /* Fill in the blank */
7. __

9. fn main() {
10. // We can refer to each variant via its alias, not its long and inconvenient
11. // name.
12. let x = Operations::Add;
13. }
  1. 🌟🌟 There are a few preserved aliases in Rust, one of which can be used in impl blocks.
1. enum VeryVerboseEnumOfThingsToDoWithNumbers {
2. Add,
3. Subtract,
4. }

6. impl VeryVerboseEnumOfThingsToDoWithNumbers {
7. fn run(&self, x: i32, y: i32) -> i32 {
8. match self {
9. __::Add => x + y,
10. __::Subtract => x - y,
11. }
12. }
13. }

DST and unsized type

These concepts are complicated, so we are not going to explain here, but you can find them in The Book.

  1. 🌟🌟🌟 Array with dynamic length is a Dynamic Sized Type ( DST ), we can’t directly use it
1. /* Make it work with const generics */
2. fn my_function(n: usize) -> [u32; usize] {
3. [123; n]
4. }

6. fn main() {
7. let arr = my_function();
8. println!("{:?}",arr);
9. }
  1. 🌟🌟 Slice is unsized type, but the reference of slice is not.
1. /* Make it work with slice references */
2. fn main() {
3. let s: str = "Hello there!";

5. let arr: [u8] = [1, 2, 3];
6. }
  1. 🌟🌟 Trait is also an unsized type
1. /* Make it work in two ways */
2. use std::fmt::Display;
3. fn foobar(thing: Display) {}

5. fn main() {
6. }