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

22 октомври 2024

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

Преговор

Generic Types (Generics)

Generic Types (Generics)

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

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

Generics

функции

Позволяват да пишем код, валиден за различни ситуации

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

fn identity_i8(value: u8) -> u8 {
    value
}
fn main() {}
fn identity_i32(value: i32) -> i32 {
    value
}

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

Generics

функции

Позволяват да пишем код, валиден за различни ситуации

1 2 3
fn identity<T>(value: T) -> T {
    value
}
fn main() {}
fn identity(value: T) -> T {
    value
}

Generics

функции

Можем да пишем по-сложни функции..

1 2 3
fn sum<T>(a: T, b: T) -> T {
    a + b
}

Generics

функции

Можем да пишем по-сложни функции.. ама всъщност не

1 2 3
fn sum<T>(a: T, b: T) -> T {
    a + b
}
error[E0369]: cannot add `T` to `T` --> src/bin/main_ac0e659fcafa1345b52e1d46ce02b96c1d43fbfb.rs:4:7 | 4 | a + b | - ^ - T | | | T | help: consider restricting type parameter `T` | 3 | fn sum<T: std::ops::Add<Output = T>>(a: T, b: T) -> T { | +++++++++++++++++++++++++++ For more information about this error, try `rustc --explain E0369`. error: could not compile `rust` (bin "main_ac0e659fcafa1345b52e1d46ce02b96c1d43fbfb") due to 1 previous error
fn main() {}
fn sum(a: T, b: T) -> T {
    a + b
}

Generics

функции

Generics

функции

Generics

функции

Generics

функции

Generics

функции

Generics

структури

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 };
}

Generics

структури

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

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_string = 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_string = Point { x: 5, y: "4.0" };
}

Generics

енумерации

1 2 3 4 5 6 7 8 9
enum Message<T, A> {
    Text(T),
    Action(A),
}

enum Option<T> {
    Some(T),
    None,
}
fn main() {}
enum Message {
    Text(T),
    Action(A),
}

enum Option {
    Some(T),
    None,
}

Generics

методи

1 2 3 4 5 6 7 8 9 10 11 12 13
struct Point<T> { x: T, y: T }

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

fn main() {
    let p = Point { x: 5, y: 10 };
    println!("coords = {:?}", p.coords());
}
coords = (5, 10)
struct Point { x: T, y: T }

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

fn main() {
    let p = Point { x: 5, y: 10 };
    println!("coords = {:?}", p.coords());
}

Generics

методи

impl<T> Point<T> { ... }

означава

за всяко Т: impl Point<T> { ... }

Generics

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

1 2 3 4 5 6 7 8 9 10 11 12 13
struct Point<T> { x: T, y: T }

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

fn main() {
    println!("dist = {:?}", Point { x: 5.0, y: 10.0 }.dist_from_origin());
    // println!("dist = {:?}", Point { x: 5, y: 10 }.dist_from_origin());
}
struct Point { x: T, y: T }

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

fn main() {
    println!("dist = {:?}", Point { x: 5.0, y: 10.0 }.dist_from_origin());
    // println!("dist = {:?}", Point { x: 5, y: 10 }.dist_from_origin());
}

Generics

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

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

// за всяко Т: impl Point<T, T>
impl<T> Point<T, T> {
    fn coords_1(&self) -> (&T, &T) {
        (&self.x, &self.y)
    }
}

// за всяко T, за всяко U: impl Point<T, U>
impl<T, U> Point<T, U> {
    fn coords_2(&self) -> (&T, &U) {
        (&self.x, &self.y)
    }
}
struct Point { x: T, y: U }

// за всяко Т: impl Point
impl Point {
    fn coords_1(&self) -> (&T, &T) {
        (&self.x, &self.y)
    }
}

// за всяко T, за всяко U: impl Point
impl Point {
    fn coords_2(&self) -> (&T, &U) {
        (&self.x, &self.y)
    }
}

fn main() {}

Generics

шаблонни методи

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
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'
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);
}

Константни шаблони

1 2 3 4 5 6 7
fn f<T, const N: usize>(_a: [T; N]) { }

fn main() {
    f::<String, 1>([
        String::from("hello"),
    ]);
}
fn f(_a: [T; N]) { }

fn main() {
    f::([
        String::from("hello"),
    ]);
}

Упражнение

The JSON encoder

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

Упражнение

The JSON encoder

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

Упражнение

The JSON encoder

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

Типажи

Traits

Типажи

Traits

Типажи

Traits

Типажи

Traits

Упражнение

The JSON encoder

Дефинираме си trait:

1 2 3
trait ToJson {
    fn to_json(&self) -> String;
}
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)
    }
}
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)
    }
}
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("mama").to_json());

println!("Number as json: {}", 3.to_json());
String as json: "mama" 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("mama").to_json());

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

Отклонение

макрото format!

Упражнение

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());
}

Упражнение

Ограничения (type bounds)

Още малко - за 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"),
        }
    }
}
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 bounds)

Още малко - за 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"),
        }
    }
}
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.

Упражнение

Ограничения (type bounds)

1 2
impl<T: ToJson> ToJson for Option<T> {
}

е еквиваленто на

1 2 3 4 5
impl<T> ToJson for Option<T>
where
    T: ToJson,
{
}

Упражнение

The JSON encoder

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

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

        let mut result = match iter.next() {
            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() {}
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 mut result = match iter.next() {
            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>
}
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()
        )
    }
}
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()
}
trait ToJson { fn to_json(&self) -> String; }
fn main() {}
fn to_json(value: T) -> String {
    value.to_json()
}

Traits

множество типажи

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

1 2 3 4 5 6
fn log_json_transformation<T>(value: T)
where
    T: ToJson + Debug,
{
    println!("{:?} -> {}", value, value.to_json());
}
use std::fmt::Debug;
trait ToJson { fn to_json(&self) -> String; }
fn main() {}
fn log_json_transformation(value: T)
where
    T: ToJson + Debug,
{
    println!("{:?} -> {}", value, value.to_json());
}

Traits

множество типажи

1 2 3 4 5
fn log_json_transformation<T>(value: T)
where
    T: ToJson + Debug,
{
}
use std::fmt::Debug;
trait ToJson { fn to_json(&self) -> String; }
fn main() {}
fn log_json_transformation(value: T)
where
    T: ToJson + Debug,
{
}

е еквивалентно на

1 2 3 4 5 6
fn log_json_transformation<T>(value: T)
where
    T: ToJson,
    T: Debug,
{
}
use std::fmt::Debug;
trait ToJson { fn to_json(&self) -> String; }
fn main() {}
fn log_json_transformation(value: T)
where
    T: ToJson,
    T: Debug,
{
}

Traits

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

Traits

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

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

Traits

static dispatch

Traits

static dispatch

1 2 3
fn to_json<T: ToJson>(value: T) -> String {
    value.to_json()
}
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
fn to_json(value: T) -> String {
    value.to_json()
}

Traits

static dispatch

1 2 3
fn to_json<T: ToJson>(value: T) -> String {
    value.to_json()
}
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
fn to_json(value: T) -> String {
    value.to_json()
}

Traits

static dispatch

1 2 3
fn to_json<T: ToJson>(value: T) -> String {
    value.to_json()
}
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
fn to_json(value: T) -> String {
    value.to_json()
}

Traits

static dispatch

1 2 3
fn to_json<T: ToJson>(value: T) -> String {
    value.to_json()
}
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
fn to_json(value: T) -> String {
    value.to_json()
}

Traits

static dispatch

1 2 3
fn to_json<T: ToJson>(value: T) -> String {
    value.to_json()
}
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
fn to_json(value: T) -> String {
    value.to_json()
}

Turbofish!

Trait Objects

dynamic dispatch

1 2 3
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}

Trait Objects

dynamic dispatch

1 2 3
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}

Trait Objects

dynamic dispatch

1 2 3
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}

Trait Objects

dynamic dispatch

1 2 3
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}
fn main() {}
trait ToJson { fn to_json(&self) -> String; }
fn to_json(value: &dyn ToJson) -> String {
    value.to_json()
}

Trait Objects

dynamic dispatch

Trait Objects

dynamic dispatch

Trait Objects

dynamic dispatch

Trait Objects

dynamic dispatch

Trait Objects

dynamic dispatch

Trait Objects

dynamic dispatch

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>());
}

Trait Objects

dynamic dispatch

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

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
impl ToJson for Box<dyn ToJson> {
    fn to_json(&self) -> String {
        (**self).to_json()
    }
}

fn main() {
    let values = vec![
        Box::new(1.1_f32) as Box<dyn ToJson>,
        Box::new(3_i32),
        Box::new(String::from("Stuff")),
    ];

    println!("{}", to_json(&values));
}
[1.1, 3, "Stuff"]
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) } }
impl ToJson for String { fn to_json(&self) -> String { format!("{:?}", self) } }
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 to_json(value: &dyn ToJson) -> String { value.to_json() }
impl ToJson for Box {
    fn to_json(&self) -> String {
        (**self).to_json()
    }
}

fn main() {
    let values = vec![
        Box::new(1.1_f32) as Box,
        Box::new(3_i32),
        Box::new(String::from("Stuff")),
    ];

    println!("{}", to_json(&values));
}

Trait Objects

object safety

Trait Objects

object safety

Trait Objects

object safety

Trait Objects

object safety

Примери за неща, които правят trait-а не-object-safe

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
trait NotObjectSafe {
    type Item;

    // шаблонни функции
    fn generic<T>(&self, val: &T);

    // функции, които приемат аргумент от тип Self или връщат Self
    fn receiver_by_value(self);
    fn self_argument(&self, other: Self);
    fn duplicate(&self) -> Self;

    // функции, които приемат или връщат асоцииран тип
    fn get_item(&self) -> &Self::Item;
    fn set_item(&mut self, item: &Self::Item);

    // и други
}
fn main() {}
trait NotObjectSafe {
    type Item;

    // шаблонни функции
    fn generic(&self, val: &T);

    // функции, които приемат аргумент от тип Self или връщат Self
    fn receiver_by_value(self);
    fn self_argument(&self, other: Self);
    fn duplicate(&self) -> Self;

    // функции, които приемат или връщат асоцииран тип
    fn get_item(&self) -> &Self::Item;
    fn set_item(&mut self, item: &Self::Item);

    // и други
}

Trait Objects

Хитрина - можем да направим trait object-safe, ако сложим ограничение where Self: Sized на проблематичните функции

1 2 3 4
trait ObjectSafe {
    fn to_json(&self) -> String;
    fn to_bytes(self) -> Vec<u8> where Self: Sized;
}
fn main() {}
trait ObjectSafe {
    fn to_json(&self) -> String;
    fn to_bytes(self) -> Vec where Self: Sized;
}

Trait Objects

Хитрина - можем да направим trait object-safe, ако сложим ограничение where Self: Sized на проблематичните функции

1 2 3 4
trait ObjectSafe {
    fn to_json(&self) -> String;
    fn to_bytes(self) -> Vec<u8> where Self: Sized;
}
fn main() {}
trait ObjectSafe {
    fn to_json(&self) -> String;
    fn to_bytes(self) -> Vec where Self: Sized;
}

Trait Objects

Хитрина - можем да направим trait object-safe, ако сложим ограничение where Self: Sized на проблематичните функции

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
trait ObjectSafe {
    fn to_json(&self) -> String;
    fn to_bin(self) -> Vec<u8> where Self: Sized;
}
fn main() {
    let string = String::from("foo");

    let trait_obj = &string as &dyn ObjectSafe;
    println!("{}", trait_obj.to_json());
    // error: the `to_bin` method cannot be invoked on a trait object
    // println!("{:?}", trait_obj.to_bin());

    println!("{}", string.to_json());
    println!("{:x?}", string.to_bin());
}
"foo" "foo" [66, 6f, 6f]
impl ObjectSafe for String {
fn to_json(&self) -> String { format!("{:?}", self) }
fn to_bin(self) -> Vec where Self: Sized { self.as_bytes().to_owned() }
}
trait ObjectSafe {
    fn to_json(&self) -> String;
    fn to_bin(self) -> Vec where Self: Sized;
}
fn main() {
    let string = String::from("foo");

    let trait_obj = &string as &dyn ObjectSafe;
    println!("{}", trait_obj.to_json());
    // error: the `to_bin` method cannot be invoked on a trait object
    // println!("{:?}", trait_obj.to_bin());

    println!("{}", string.to_json());
    println!("{:x?}", string.to_bin());
}

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

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

Позволяват задаване на различен тип за всяка имплементрация на trait-а

1 2 3 4 5
trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
}

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

1 2 3 4 5 6 7 8 9 10 11
impl Iterator for std::str::Chars {
    type Item = char;

    fn next(&mut self) -> Option<Self::Item> { ... }
}

impl Iterator for std::str::Bytes {
    type Item = u8;

    fn next(&mut self) -> Option<Self::Item> { ... }
}

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

и шаблонни типажи (generic traits)

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

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

    fn add(self, rhs: RHS) -> Self::Output;
}
fn main() {}
trait Add {
    type Output;

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

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

и шаблонни типажи (generic traits)

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

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

    fn add(self, rhs: RHS) -> Self::Output;
}
fn main() {}
trait Add {
    type Output;

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

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

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

и шаблонни типажи (generic 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)
    }
}
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)
    }
}

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

и шаблонни типажи (generic 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] }
    }
}
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] }
    }
}

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

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

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

1 2 3
trait Add {
    fn add(self, rhs: Self) -> Self;
}
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

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

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

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

1 2 3
trait Add<RHS> {
    fn add(self, rhs: RHS) -> Self;
}
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) -> RHS;

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

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

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

1 2 3
trait Add<RHS, OUTPUT> {
    fn add(self, rhs: RHS) -> OUTPUT;
}
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 = ...

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

Каква е разликата между "асоцииран тип" и "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 { ... }
}

Заключение

Въпроси