06. Шаблонни типове, типажи

06. Шаблонни типове, типажи

06. Шаблонни типове, типажи

25 октомври 2017

Преговор

Въпроси

enum Act {
    TooAwesome { name: String, reason: String },
    Awesome(String),
    Lame,
}
fn pass_the_act(_: &Act) { /*...*/ }

fn main() {
    let act = Act::Lame;
    match act {
        Act::TooAwesome { name, ... } => println!("{} е върхът!", name),
        Act::Awesome { name }         => println!("{} е супер!", name),
        Act::Lame                     => println!("Кофти.."),
    }

    pass_the_act(&act);
}

Отговори

enum Act {
    TooAwesome { name: String, reason: String },
    Awesome(String),
    Lame,
}
fn pass_the_act(_: &Act) { /*...*/ }

fn main() {
    let act = Act::Lame;
    match act {
        Act::TooAwesome { ref name, .. } => println!("{} е върхът!", name),
        Act::Awesome(ref name)           => println!("{} е супер!", name),
        Act::Lame                        => println!("Кофти.."),
    }

    pass_the_act(&act);
}

Generic Types (Generics)

Generic Types (Generics)

Oбобщени типове

Вече сме ги виждали

Oбобщени типове

Oбобщени типове

функции

Със знанията събрани до сега

fn add_i32(a: i32, b: i32) -> i32 {
    a + b
}

fn add_u8(a: u8, b: u8) -> u8 {
    a + b
}

Oбобщени типове

функции

С обобщени типове

fn add<T>(a: T, b: T) -> T {
    a + b
}

Oбобщени типове

функции

Получаваме

error[E0369]: binary operation `+` cannot be applied to type `T`
  |
2 |     a + b
  |     ^^^^^
  |
  = note: an implementation of `std::ops::Add` might be missing for `T`

Ще видим как да оправим грешката малко по-късно в презентацията

Oбобщени типове

структури

Нека разгледаме структурата

struct Point<T> {
    x: T,
    y: T,
}

fn main() {
    let integer = Point { x: 5, y: 10 };
    let float = Point { x: 1.0, y: 4.0 };
}

Oбобщени типове

структури

А какво ще стане, ако опитаме да я създадем по този начин?

fn main() {
    let what_about_this = Point { x: 5, y: 4.0 }; // ??
}

Oбобщени типове

разпознаване на тип

error[E0308]: mismatched types
  |
9 |     let what_about_this = Point { x: 5, y: 4.0 };
  |                                            ^^^ expected integral variable,
                                                   found floating-point variable
  |
  = note: expected type `{integer}`
             found type `{float}`

Oбобщени типове

структури

Ако искаме двете координати да могат да са различни типове

struct Point<T, U> {
    x: T,
    y: U,
}

fn main() {
    let both_integer = Point { x: 5, y: 10 };
    let both_float = Point { x: 1.0, y: 4.0 };
    let integer_and_float = Point { x: 5, y: 4.0 };
}

Oбобщени типове

енумерации

Миналия път разгледахме какво прави Option

Това е дефиницията му:

enum Option<T> {
    Some(T),
    None,
}

Oбобщени типове

енумерации

enum Message<T, A> {
    Text(T),
    Action(A),
}

Oбобщени типове

методи

struct Point<T> { x: T, y: T }

// Забележете impl<T>
impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

fn main() {
    let p = Point { x: 5, y: 10 };

    println!("p.x   = {}", p.x);    // ??
    println!("p.x() = {}", p.x());  // ??
}

Oбобщени типове

методи

Стандартен изход на примера

p.x   = 5
p.x() = 5

Oбобщени типове

методи

За любознателните - има начин как да се вземе адрес към property-то чрез абсолютен път

println!("p.x = {:x}", Point::<u32>::x as usize);
println!("p.x = {:x}", Point::<f64>::x as usize);

Turbofish! → ::<>

Turbofish!

Oбобщени типове

специализирани имплементации

В този пример само Point<f32> ще притежава този метод

// Този път няма impl<T>
impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

Oбобщени типове

struct Point<T, U> {
    x: T,
    y: U,
}

impl<T, U> Point<T, U> {
    fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
        Point { x: self.x, y: other.y }
    }
}

fn main() {
    let p1 = Point { x: 5, y: 10.4 };
    let p2 = Point { x: "Hello", y: 'c'};
    let p3 = p1.mixup(p2);
    println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

Упражнение

The JSON encoder

Упражнение

The JSON encoder

fn to_json<T>(val: T) -> String {
  ...
}

Типажи

Traits

Типажи

Traits

Упражнение

The JSON encoder

Нека си дефинираме trait ToJSON:

trait ToJSON {
    fn to_json(&self) -> String;
}

Упражнение

The JSON encoder

Сега можем да го имплементираме за някои вградени типове данни:

impl ToJSON for String {
    fn to_json(&self) -> String {
        format!("\"{}\"", self)
    }
}

Упражнение

The JSON encoder

Сега можем да го имплементираме за някои вградени типове данни:

impl ToJSON for i32 {
    fn to_json(&self) -> String {
        format!("{}", self)
    }
}

impl ToJSON for f32 {
    fn to_json(&self) -> String {
        format!("{}", self)
    }
}

Упражнение

The JSON encoder

println!("String as json: {}", String::from("mamal").to_json());
// String as json: "mamal"

println!("Number as json: {}", 3.to_json());
// Number as json: 3

Упражнение

The JSON encoder

Между другото, можем да имаме имплементация по подразбиране:

trait ToJSON {
  fn to_json(&self) -> String {
      String::from("null")
  }
}

impl ToJSON for () {}

println!("Unit as json: {}", ().to_json());
// Unit as json: null

Упражнение

The JSON encoder

Още малко - за Option!

impl <T> ToJSON for Option<T> where T : ToJSON {
    fn to_json(&self) -> String {
        match self {
            &Some(ref val) => val.to_json(),
            &None => String::from("null")
        }
    }
}

Упражнение

The JSON encoder

В JSON има списъци, нека да пробваме да го направим за вектор:

impl <T> ToJSON for Vec<T> where T: ToJSON {
  fn to_json(&self) -> String {
      let mut result: String = String::from("");
      for e in self.iter() {
          result.push_str(&e.to_json());
          result.push_str(",");
      }
      result.pop();

      format!("[{}]", result)
  }
}

Упражнение

The JSON encoder

В JSON има списъци, нека да пробваме да го направим за вектор:

println!("Vector as json: {}", vec![Some(1.1), Some(2.2), None].to_json());
// Vector as json: [1.1, 2.2, null]

Упражнение

The JSON encoder

А сега и за наш си тип:

struct Student {
    age: i32,
    full_name: String,
    number: i32,
    hobby: Option<String>
}

Упражнение

The JSON encoder

impl ToJSON for Student {
    fn to_json(&self) -> String {
        format!(
            r#"
            {{
                "age": {},
                "fullName": {},
                "number": {},
                "hobby": {}
            }}"#,
            self.age.to_json(), self.full_name.to_json(),
            self.number.to_json(), self.hobby.to_json()
            )
    }
}

Упражнение

The JSON encoder

Сега можем да си дефинираме функцията, от която почна всичко:

fn to_json<T: ToJSON>(value: T) -> String {
    value.to_json()
}

Типажи

Traits

А ако искаме дадена стойност да имплементира повече от един trait?

fn log_json_transformation<T: ToJSON + Debug>(value: T) {
    println!("{:?} -> {}", value, value.to_json());
}

Traits

Кога можем да имлементираме trait?

Можем да имплементираме trait T за тип S ако:

Traits

static dispatch

Traits

dynamic dispatch

Има начин да се използва една версия на функцията и runtime тя да се избира.

Това става с trait обекти - ако имаме trait Stuff, &Stuff да представлява какъвто и да е обект имплементиращ trait-а.

fn to_json(value: &ToJSON) -> String {
    value.to_json()
}

let trait_object: &ToJSON = &5;

println!("{}", to_json(trait_object));

to_json(&5);
to_json(&5 as &ToJSON);

Traits

dynamic dispatch

Trait Objects

Можем да използваме trait обекти да си направим не-хомогенен вектор, който може да се принтира.

use std::fmt::Debug;

println!("{:?}", vec![
    &1.1 as &Debug,
    &Some(String::from("Stuff")),
    &3
]);

Traits

Асоциирани типове

Нека си дефинираме trait за събиране:

trait Add<RHS=Self> {
    type Output;

    fn add(self, rhs: RHS) -> Self::Output;
}

Traits

Асоциирани типове

impl Add for i32 {
    type Output = i32;

    fn add(self, rhs: i32) -> i32 {
        self + rhs
    }
}

impl Add for String {
    type Output = String;

    fn add(self, rhs: String) -> String {
        format!("{} {}", self, rhs)
    }
}

Traits

Асоциирани типове

Асоциираният тип ни дава повече възможности.

Да речем резултата от събирането, може да е друг тип:

struct StudentGroup {
    members: Vec<Student>
}

impl Add for Student {
    type Output = StudentGroup;

    fn add(self, rhs: Student) -> StudentGroup {
        StudentGroup { members: vec![self, rhs] }
    }
}

Въпроси