1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//! Трейты для преобразования типов.
//! 
//! Трейты  [`Of`]/[`To`] являются заменой [`From`]/[`Into`] из стандартной библиотеки и имеют более функциональный вид.
//!
//! Данная реализация [`From`]/[`Into`] может быть полензна, когда мы хотим сохранить функциональный стиль, при том что у нашего типа есть множество реализаций трейта [`From`].
//!
//! # Стандартная реализация с [`From`]/[`Into`]
//! Когда у типа `TypeInto` появляется более одной реализации типажа [`From`], нам необходимо указать компилятору, какую из реализаций мы используем в данный момент.
//! В стандартной реализации [`From`]/[`Into`]  это можно сделать с помощью следующих конструкций:
//! - `<TypeFrom as Into<TypeInto>>::`
//! - `Into::<TypeInto>>::`
//! , где `TypeFrom` - тип, который укзан в реализации как `impl From<TypeFrom> for TypeInto`.
//!
//! ## Пример
//! ```
//! struct Rubles;
//! struct Euros;
//! struct Dollars;
//! 
//! impl From<Rubles> for Dollars {
//!     fn from(value: Rubles) -> Self {
//!         Dollars
//!     }
//! }
//! 
//! impl From<Euros> for Dollars {
//!     fn from(value: Euros) -> Self {
//!         Dollars
//!     }
//! }
//! 
//! fn from_into() {
//!     let dollars = <Euros as Into<Dollars>>::into(Euros);
//!     let dollars = Into::<Dollars>::into(Euros);
//! }
//! ```
//!
//! # Реализацис c [`Of`]/[`To`]
//!
//! Для того чтобы указать компилятору сигнатуру опрделеной реализации трейта, мы можем указать границы не только для самого трейта (высокий уровень), но и для функций этого трейта (низкий уровень).
//! Так в трейте [`To`], generic типа, в который мы выполняем преобразование, указывается для метода `to`.
//! Теперь, чтобы указать конкртеную реализацию, нам достаточно конретизировать метод трейта:
//! - `TypeFrom.to::<TypeInto>()`
//!
//! ## Пример
//! ```
//! # use std_reset::prelude::{Of, To};
//! #
//! # struct Rubles;
//! # struct Euros;
//! # struct Dollars;
//! #
//! impl Of<Rubles> for Dollars {
//!     fn of(value: Rubles) -> Self {
//!         Dollars
//!     }
//! }
//! 
//! impl Of<Euros> for Dollars {
//!     fn of(value: Euros) -> Self {
//!         Dollars
//!     }
//! }
//! 
//! fn of_to() {
//!     let dollars = Rubles.to::<Dollars>();
//!     let dollars = Euros.to::<Dollars>();
//! }
//! ```
pub trait Of<F>: To {
    fn of(value: F) -> Self;
}

pub trait To {
    fn to<I: Of<Self>>(self) -> I
    where
        Self: Sized;
}

impl<F> To for F {
    fn to<I: Of<F>>(self) -> I {
        I::of(self)
    }
}