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

29 октомври 2019

Административни неща

Административни неща

Административни неща

Преговор

Документация

1 2 3 4 5 6 7 8 9 10 11
/// Add 2 to a number
///
/// # Example
///
/// ```
/// # use playground::add_two;
/// assert_eq!(add_two(5), 7);
/// ```
pub fn add_two(n: u32) -> u32 {
    n + 2
}
/// Add 2 to a number
///
/// Example
///
/// ```
/// use playground::add_two;
/// assert_eq!(add_two(5), 7);
/// ```
pub fn add_two(n: u32) -> u32 {
    n + 2
}
fn main() {}

Преговор

Тестове

1 2 3 4 5 6 7 8 9 10 11
fn add_two(n: u32) -> u32 {
    n + 2
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(add_two(2), 4);
    }
}
#![allow(dead_code)]
fn main() {}
fn add_two(n: u32) -> u32 {
    n + 2
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(add_two(2), 4);
    }
}

Преговор

Атрибути

Атрибути

Документация

1 2 3 4 5 6
/// Add 2 to a number
///
/// # Example
pub fn add_two(n: u32) -> u32 {
    n + 2
}
/// Add 2 to a number
///
/// Example
pub fn add_two(n: u32) -> u32 {
    n + 2
}
fn main() {}

Атрибути

Документация

1 2 3 4 5 6
#[doc="Add 2 to a number"]
#[doc=""]
#[doc="# Example"]
pub fn add_two(n: u32) -> u32 {
    n + 2
}
#[doc="Add 2 to a number"]
#[doc=""]
#[doc="Example"]
pub fn add_two(n: u32) -> u32 {
    n + 2
}
fn main() {}

Generic Types (Generics)

Generic Types (Generics)

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

Generic Types (Generics)

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

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

Generic Types (Generics)

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

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

Generic Types (Generics)

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

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

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

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

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

функции

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

1 2 3 4 5 6 7
fn id_i32(value: i32) -> i32 {
    value
}

fn id_i8(value: u8) -> u8 {
    value
}
#![allow(dead_code)]
fn main() {}
fn id_i32(value: i32) -> i32 {
    value
}

fn id_i8(value: u8) -> u8 {
    value
}

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

функции

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

1 2 3
fn id<T>(value: T) -> T {
    value
}
#![allow(dead_code)]
fn main() {}
fn id(value: T) -> T {
    value
}

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

структури

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

1 2 3 4 5 6 7 8 9 10 11 12
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 };
}
#![allow(dead_code, unused_variables)]
struct Point {
    x: T,
    y: T,
}

fn main() {
    // Може да я създадем с цели числа..
    let integer = Point { x: 5, y: 10 };

    // ..но може да я създадем и с числа с плаваща запетая.
    let float = Point { x: 1.0, y: 4.0 };
}

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

структури

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

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

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

структури

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

1 2 3
fn main() {
    let what_about_this = Point { x: 5, y: 4.0 };
}
error[E0308]: mismatched types --> src/bin/main_2f97ad6350948c3ad36abcbec26aa61d24df52ce.rs:5:44 | 5 | let what_about_this = Point { x: 5, y: 4.0 }; | ^^^ expected integer, found floating-point number | = note: expected type `{integer}` found type `{float}`
#![allow(dead_code)]
struct Point { x: T, y: T }
fn main() {
    let what_about_this = Point { x: 5, y: 4.0 };
}

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

структури

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

1 2 3 4 5 6 7 8 9 10
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 };
}
#![allow(dead_code, unused_variables)]
struct Point {
    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бобщени типове

енумерации

Енумерациите с обобщени типове имат подобен вид:

1 2 3 4
enum Message<T, A> {
    Text(T),
    Action(A),
}
#![allow(dead_code)]
fn main() {}
enum Message {
    Text(T),
    Action(A),
}

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

енумерации

Ето как се дефинира Option:

1 2 3 4
enum Option<T> {
    Some(T),
    None,
}
#![allow(dead_code)]
fn main() {}
enum Option {
    Some(T),
    None,
}

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

методи

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
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());  // ??
}
#![allow(dead_code)]
struct Point { x: T, y: T }

// Забележете impl
impl Point {
    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бобщени типове

методи

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
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());
}
p.x = 5 p.x() = 5
#![allow(dead_code)]
struct Point { x: T, y: T }

// Забележете impl
impl Point {
    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бобщени типове

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

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

1 2 3 4 5 6
// Този път няма impl<T>
impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}
#![allow(dead_code)]
struct Point { x: T, y: T }
fn main() {}
// Този път няма impl
impl Point {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
struct Point<T, U> {
    x: T,
    y: U,
}

impl<T, U> Point<T, U> {
    // Създава нова структура с `x` от `self` и `y` от `other`.
    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.x);
    println!("p3.y = {}", p3.y);
}
p3.x = 5 p3.y = c
#![allow(dead_code)]
struct Point {
    x: T,
    y: U,
}

impl Point {
    // Създава нова структура с `x` от `self` и `y` от `other`.
    fn mixup(self, other: Point) -> Point {
        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.x);
    println!("p3.y = {}", p3.y);
}

Упражнение

The JSON encoder

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

Упражнение

The JSON encoder

Тук възникват няколко въпроса:

Упражнение

The JSON encoder

Тук възникват няколко въпроса:

Типажи

Traits

Типажи

Traits

Типажи

Traits

Типажи

Traits

Типажи

Traits

Упражнение

The JSON encoder

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

1 2 3
trait ToJson {
    fn to_json(&self) -> String;
}
#![allow(dead_code)]
fn main() {}
trait ToJson {
    fn to_json(&self) -> String;
}

Упражнение

The JSON encoder

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

1 2 3 4 5
impl ToJson for String {
    fn to_json(&self) -> String {
        format!("\"{}\"", self)
    }
}
#![allow(dead_code)]
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
impl ToJson for String {
    fn to_json(&self) -> String {
        format!("\"{}\"", self)
    }
}

Упражнение

The JSON encoder

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

1 2 3 4 5 6 7 8 9 10 11
impl ToJson for i32 {
    fn to_json(&self) -> String {
        format!("{}", self)
    }
}

impl ToJson for f32 {
    fn to_json(&self) -> String {
        format!("{}", self)
    }
}
#![allow(dead_code)]
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
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

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

println!("Number as json: {}", 3.to_json());
String as json: "mamal" Number as json: 3
trait ToJson {
fn to_json(&self) -> String;
}
impl ToJson for String {
fn to_json(&self) -> String {
format!(r#""{}""#, self)
}
}
impl ToJson for i32 {
fn to_json(&self) -> String {
format!("{}", self)
}
}
fn main() {
println!("String as json: {}", String::from("mamal").to_json());

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

Упражнение

The JSON encoder

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

1 2 3 4 5 6 7 8 9 10 11
trait ToJson {
    fn to_json(&self) -> String {
        String::from("null")
    }
}

impl ToJson for () {}

fn main() {
    println!("Unit as json: {}", ().to_json());
}
Unit as json: null
trait ToJson {
    fn to_json(&self) -> String {
        String::from("null")
    }
}

impl ToJson for () {}

fn main() {
    println!("Unit as json: {}", ().to_json());
}

Упражнение

The JSON encoder

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

1 2 3 4 5 6 7 8
impl<T> ToJson for Option<T> where T: ToJson {
    fn to_json(&self) -> String {
        match self {
            Some(val) => val.to_json(),
            None => String::from("null"),
        }
    }
}
#![allow(dead_code)]
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
impl ToJson for Option where T: ToJson {
    fn to_json(&self) -> String {
        match self {
            Some(val) => val.to_json(),
            None => String::from("null"),
        }
    }
}

Упражнение

The JSON encoder

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

1 2 3 4 5 6 7 8
impl<T> ToJson for Option<T> where T: ToJson {
    fn to_json(&self) -> String {
        match self {
            Some(val) => val.to_json(),
            None => String::from("null"),
        }
    }
}
#![allow(dead_code)]
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
impl ToJson for Option where T: ToJson {
    fn to_json(&self) -> String {
        match self {
            Some(val) => val.to_json(),
            None => String::from("null"),
        }
    }
}

Забележете, че използваме type bound T: ToJson, за да работи функцията само върху Option, който съдържа стойност имплементираща ToJson.

Упражнение

The JSON encoder

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
impl<T> ToJson for Vec<T> where T: ToJson {
    fn to_json(&self) -> String {
        let mut iter = self.iter();
        let first = iter.next();

        let mut result = match first {
            Some(first) => first.to_json(),
            None => String::new(),
        };

        for e in iter {
            result.push_str(", ");
            result.push_str(&e.to_json());
        }

        format!("[{}]", result)
    }
}
#![allow(dead_code)]
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
impl ToJson for Option where T: ToJson {
fn to_json(&self) -> String {
match self {
&Some(ref val) => val.to_json(),
&None => String::from("null"),
}
}
}
impl<'a, T> ToJson for &'a T where T: ToJson {
fn to_json(&self) -> String {
(*self).to_json()
}
}
impl ToJson for Vec where T: ToJson {
    fn to_json(&self) -> String {
        let mut iter = self.iter();
        let first = iter.next();

        let mut result = match first {
            Some(first) => first.to_json(),
            None => String::new(),
        };

        for e in iter {
            result.push_str(", ");
            result.push_str(&e.to_json());
        }

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

Упражнение

The JSON encoder

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

1 2 3 4
fn main() {
    let arr = vec![Some(1.1), Some(2.2), None].to_json();
    println!("Vector as json: {}", arr);
}
Vector as json: [1.1, 2.2, null]
trait ToJson { fn to_json(&self) -> String; }
impl ToJson for f32 {
fn to_json(&self) -> String {
format!("{}", self)
}
}
impl ToJson for Option where T: ToJson {
fn to_json(&self) -> String {
match self {
&Some(ref val) => val.to_json(),
&None => String::from("null"),
}
}
}
impl<'a, T> ToJson for &'a T where T: ToJson {
fn to_json(&self) -> String {
(*self).to_json()
}
}
impl  ToJson for Vec where T: ToJson {
fn to_json(&self) -> String {
let mut iter = self.iter();
let first = iter.next();
let mut result = match first {
Some(first) => first.to_json(),
None => String::new(),
};
for e in iter {
result.push_str(", ");
result.push_str(&e.to_json());
}
format!("[{}]", result)
}
}
fn main() {
    let arr = vec![Some(1.1), Some(2.2), None].to_json();
    println!("Vector as json: {}", arr);
}

Упражнение

The JSON encoder

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

1 2 3 4 5 6
struct Student {
    age: i32,
    full_name: String,
    number: i32,
    hobby: Option<String>
}
#![allow(dead_code)]
fn main() {}
struct Student {
    age: i32,
    full_name: String,
    number: i32,
    hobby: Option
}

Упражнение

The JSON encoder

1 2 3 4 5 6 7 8 9 10 11 12 13 14
impl ToJson for Student {
    fn to_json(&self) -> String {
        format!(
r#"{{
    "age": {},
    "full_name": {},
    "number": {},
    "hobby": {}
}}"#,
            self.age.to_json(), self.full_name.to_json(),
            self.number.to_json(), self.hobby.to_json()
        )
    }
}
#![allow(dead_code)]
trait ToJson { fn to_json(&self) -> String; }
struct Student {
age: i32,
full_name: String,
number: i32,
hobby: Option
}
impl ToJson for i32 {
fn to_json(&self) -> String {
format!("{}", self)
}
}
impl ToJson for Option where T: ToJson {
fn to_json(&self) -> String {
match self {
&Some(ref val) => val.to_json(),
&None => String::from("null"),
}
}
}
impl ToJson for String {
fn to_json(&self) -> String {
format!("\"{}\"", self)
}
}
fn main() {}
impl ToJson for Student {
    fn to_json(&self) -> String {
        format!(
r#"{{
    "age": {},
    "full_name": {},
    "number": {},
    "hobby": {}
}}"#,
            self.age.to_json(), self.full_name.to_json(),
            self.number.to_json(), self.hobby.to_json()
        )
    }
}

Упражнение

The JSON encoder

1 2 3 4 5 6 7 8 9 10
fn main() {
    let student = Student {
        age: 16,
        full_name: "Jane Doe".to_owned(),
        number: 5,
        hobby: Some("Tennis".to_string())
    };

    println!("{}", student.to_json());
}
{ "age": 16, "full_name": "Jane Doe", "number": 5, "hobby": "Tennis" }
trait ToJson { fn to_json(&self) -> String; }
struct Student {
age: i32,
full_name: String,
number: i32,
hobby: Option
}
impl ToJson for i32 {
fn to_json(&self) -> String {
format!("{}", self)
}
}
impl ToJson for Option where T: ToJson {
fn to_json(&self) -> String {
match self {
&Some(ref val) => val.to_json(),
&None => String::from("null"),
}
}
}
impl ToJson for String {
fn to_json(&self) -> String {
format!("\"{}\"", self)
}
}
impl ToJson for Student {
fn to_json(&self) -> String {
format!(
r#"{{
"age": {},
"full_name": {},
"number": {},
"hobby": {}
}}"#,
self.age.to_json(), self.full_name.to_json(),
self.number.to_json(), self.hobby.to_json()
)
}
}
fn main() {
    let student = Student {
        age: 16,
        full_name: "Jane Doe".to_owned(),
        number: 5,
        hobby: Some("Tennis".to_string())
    };

    println!("{}", student.to_json());
}

Упражнение

The JSON encoder

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

1 2 3
fn to_json<T: ToJson>(value: T) -> String {
    value.to_json()
}
#![allow(dead_code)]
trait ToJson { fn to_json(&self) -> String; }
fn main() {}
fn to_json(value: T) -> String {
    value.to_json()
}

Типажи

Traits

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

1 2 3
fn log_json_transformation<T: ToJson + Debug>(value: T) {
    println!("{:?} -> {}", value, value.to_json());
}
#![allow(dead_code)]
use std::fmt::Debug;
trait ToJson { fn to_json(&self) -> String; }
fn main() {}
fn log_json_transformation(value: T) {
    println!("{:?} -> {}", value, value.to_json());
}

Traits

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

Traits

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

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

Traits

static dispatch

Traits

static dispatch

Traits

static dispatch

Traits

static dispatch

Traits

static dispatch

Traits

static dispatch

Trait Objects

dynamic dispatch

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

Trait Objects

dynamic dispatch

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

Trait Objects

dynamic dispatch

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

1 2 3 4 5 6 7 8 9 10 11
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}

fn main() {
    let trait_object: &dyn ToJson = &5;

    println!("{}", to_json(trait_object));
    println!("{}", to_json(&5));
    println!("{}", to_json(&5 as &dyn ToJson));
}
5 5 5
trait ToJson { fn to_json(&self) -> String; }
impl ToJson for i32 {
fn to_json(&self) -> String {
format!("{}", self)
}
}
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}

fn main() {
    let trait_object: &dyn ToJson = &5;

    println!("{}", to_json(trait_object));
    println!("{}", to_json(&5));
    println!("{}", to_json(&5 as &dyn ToJson));
}

Trait Objects

dynamic dispatch

Trait Objects

dynamic dispatch

Trait Objects

dynamic dispatch

Trait Objects

dynamic dispatch

Trait Objects

dynamic dispatch

Trait Objects

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

1 2 3 4 5 6 7
use std::fmt::Debug;

println!("{:?}", vec![
    &1.1 as &dyn Debug,
    &Some(String::from("Stuff")),
    &3
]);
[1.1, Some("Stuff"), 3]
fn main() {
use std::fmt::Debug;

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

Trait Objects

Големината на един trait object е два указателя - един към самата стойност и един към vtable-a.

Може да ги срещнете още като "fat pointer".

1 2
println!("{}", mem::size_of::<&u32>());
println!("{}", mem::size_of::<&dyn Debug>());
8 16
use std::fmt::Debug;
use std::mem;
fn main() {
println!("{}", mem::size_of::<&u32>());
println!("{}", mem::size_of::<&dyn Debug>());
}

Turbofish!

Generic Traits

Нека разгледаме как бихме имплементирали Graph trait

1 2 3 4 5
trait Graph<N, E> {
    fn has_edge(&self, &N, &N) -> bool;
    fn edges(&self, &N) -> Vec<E>;
    // ...
}
#![allow(dead_code)]
fn main () {}
trait Graph {
    fn has_edge(&self, &N, &N) -> bool;
    fn edges(&self, &N) -> Vec;
    // ...
}

Generic Traits

Ако се опитаме да направим функция

1 2 3
fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> u32 {
    // ...
}
#![allow(dead_code, unused_variables)]
fn main () {}
trait Graph {
fn has_edge(&self, &N, &N) -> bool;
fn edges(&self, &N) -> Vec;
}
fn distance>(graph: &G, start: &N, end: &N) -> u32 {
    // ...
0
}

Generic Traits

Ако се опитаме да направим функция

1 2 3
fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> u32 {
    // ...
}
#![allow(dead_code, unused_variables)]
fn main () {}
trait Graph {
fn has_edge(&self, &N, &N) -> bool;
fn edges(&self, &N) -> Vec;
}
fn distance>(graph: &G, start: &N, end: &N) -> u32 {
    // ...
0
}

Тук дефиницията на типа E за ребрата на графа няма пряко отношение към сигнатурата на функцията.

Traits

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

Нека пробваме отново..

1 2 3 4 5 6 7
trait Graph {
    type N;
    type E;

    fn has_edge(&self, &Self::N, &Self::N) -> bool;
    fn edges(&self, &Self::N) -> Vec<Self::E>;
}
#![allow(dead_code)]
fn main() {}
trait Graph {
    type N;
    type E;

    fn has_edge(&self, &Self::N, &Self::N) -> bool;
    fn edges(&self, &Self::N) -> Vec;
}

Traits

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

Нека пробваме отново..

1 2 3 4 5 6 7
trait Graph {
    type N;
    type E;

    fn has_edge(&self, &Self::N, &Self::N) -> bool;
    fn edges(&self, &Self::N) -> Vec<Self::E>;
}
#![allow(dead_code)]
fn main() {}
trait Graph {
    type N;
    type E;

    fn has_edge(&self, &Self::N, &Self::N) -> bool;
    fn edges(&self, &Self::N) -> Vec;
}

Асоциираните типове служат за, един вид, групиране на типове.

Traits

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
struct Node;
struct Edge;
struct MyGraph;

impl Graph for MyGraph {
    type N = Node;
    type E = Edge;

    fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
        true
    }

    fn edges(&self, n: &Node) -> Vec<Edge> {
        Vec::new()
    }
}
#![allow(dead_code, unused_variables)]
fn main() {}
trait Graph {
type N;
type E;
fn has_edge(&self, &Self::N, &Self::N) -> bool;
fn edges(&self, &Self::N) -> Vec;
}
struct Node;
struct Edge;
struct MyGraph;

impl Graph for MyGraph {
    type N = Node;
    type E = Edge;

    fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
        true
    }

    fn edges(&self, n: &Node) -> Vec {
        Vec::new()
    }
}

Generic Traits

И сега ако се опитаме да направим функцията отново

1 2 3
fn distance<G: Graph<N=Node, E=Edge>>(graph: &G, start: &G::N, end: &G::N) -> u32 {
    // ...
}
#![allow(dead_code, unused_variables)]
fn main() {}
trait Graph {
type N;
type E;
fn has_edge(&self, &Self::N, &Self::N) -> bool;
fn edges(&self, &Self::N) -> Vec;
}
struct Node;
struct Edge;
struct MyGraph;
impl Graph for MyGraph {
type N = Node;
type E = Edge;
fn has_edge(&self, n1: &Node, n2: &Node) -> bool {
true
}
fn edges(&self, n: &Node) -> Vec {
Vec::new()
}
}
fn distance>(graph: &G, start: &G::N, end: &G::N) -> u32 {
    // ...
0
}

Traits

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

Може да си дефинираме trait за събиране като комбинираме Generic Traits и Associated Types:

1 2 3 4 5
trait Add<RHS=Self> {
    type Output;

    fn add(self, rhs: RHS) -> Self::Output;
}
#![allow(dead_code)]
fn main() {}
trait Add {
    type Output;

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

Traits

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

Може да си дефинираме trait за събиране като комбинираме Generic Traits и Associated Types:

1 2 3 4 5
trait Add<RHS=Self> {
    type Output;

    fn add(self, rhs: RHS) -> Self::Output;
}
#![allow(dead_code)]
fn main() {}
trait Add {
    type Output;

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

RHS=Self указва тип по подразбиране. Това е позволено само за struct, enum, type и trait.

Traits

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
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)
    }
}
#![allow(dead_code)]
fn main() {}
trait Add {
type Output;
fn add(self, rhs: RHS) -> Self::Output;
}
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

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

1 2 3 4 5 6 7 8 9 10 11 12
struct Student;
struct StudentGroup {
    members: Vec<Student>
}

impl Add for Student {
    type Output = StudentGroup;

    fn add(self, rhs: Student) -> StudentGroup {
        StudentGroup { members: vec![self, rhs] }
    }
}
#![allow(dead_code)]
fn main() {}
trait Add {
type Output;
fn add(self, rhs: RHS) -> Self::Output;
}
struct Student;
struct StudentGroup {
    members: Vec
}

impl Add for Student {
    type Output = StudentGroup;

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

Traits

Каква е разликата между "асоцииран тип" и "generic тип"?

Да речем, че имаме Add trait дефиниран така:

1 2 3
trait Add {
    fn add(self, rhs: Self) -> Self;
}
#![allow(dead_code)]
fn main() {}
trait Add {
    fn add(self, rhs: Self) -> Self;
}

Това ще работи само за един и същ тип отляво, отдясно и като резултат:

1 2 3
i32.add(i32) -> i32             // Self=i32
f64.add(f64) -> f64             // Self=f64
Student.add(Student) -> Student // Self=Student

Traits

Каква е разликата между "асоцииран тип" и "generic тип"?

За да варираме дясната страна:

1 2 3
trait Add<RHS> {
    fn add(self, rhs: RHS) -> Self;
}
#![allow(dead_code)]
fn main() {}
trait Add {
    fn add(self, rhs: RHS) -> Self;
}

Това ще позволи различни типове отляво и отдясно, но резултата задължително трябва да е левия:

1 2 3
i32.add(i8) -> i32                        // Self=i32, RHS=i8
f64.add(f32) -> f64                       // Self=f64, RHS=f32
StudentGroup.add(Student) -> StudentGroup // Self=StudentGroup, RHS=Student

(Или може да върнем -> RHS вместо -> Self, за да върнем задължително десния тип.)

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

Traits

Каква е разликата между "асоцииран тип" и "generic тип"?

За да сме напълно свободни:

1 2 3
trait Add<RHS, OUTPUT> {
    fn add(self, rhs: RHS) -> OUTPUT;
}
#![allow(dead_code)]
fn main() {}
trait Add {
    fn add(self, rhs: RHS) -> OUTPUT;
}

Проблема е, че това позволява:

1 2
i32.add(i8) -> i64 // Self=i32, RHS=i8, OUTPUT=i64
i32.add(i8) -> i32 // Self=i32, RHS=i8, OUTPUT=i32

Компилатора сега няма как да знае със сигурност какъв е типа на i32.add(i8). Може да е което и да е от двете. Налага се експлицитно да го укажем, или с ::<>, или с let result: i32 = ...

Traits

Каква е разликата между "асоцииран тип" и "generic тип"?

Асоциирания тип е компромисен вариант -- можем да изберем какъв е типа на output-а, но този тип е винаги един и същ за всяка двойка ляв+десен тип:

1 2 3 4
trait Add<RHS> {
    type Output;
    fn add(self, rhs: RHS) -> Self::Output;
}

Така можем да кажем:

1 2 3 4
impl Add<i8> for i32 {  // Имплементирай ми "добавяне на i8 към i32"
    type Output = i64;  // Като резултата ще е винаги i64
    fn add(self, rhs: i8) -> i64 { ... }
}

Заключение

Въпроси