1 /*! Directed Type Conversion
2 
3 This module provides sibling traits to the `std::convert` module. The standard
4 library puts the type parameter in the trait declaration, which makes those
5 traits generic and suitable for constraint clauses and function calls, but not
6 usable in indeterminate method-call positions. These traits put the type
7 parameter in the function declaration, making the trait non-generic and allowing
8 the function to be called in method-call position without ambiguity.
9 !*/
10 
11 use core::convert::TryInto;
12 
13 /** Directed Type Conversion
14 
15 This trait is an accessory to [`From`] and [`Into`]. It works by moving the
16 destination type from the trait name (`Into<Target>::into`) into the method name
17 (`Conv::conv::<Target>`). This change makes `Into<_>` the correct trait to use
18 in trait bounds and `.conv::<_>` the correct method to use in expressions.
19 
20 A `conv::<T>` method is automatically available whenever an `Into<T>`
21 implementation exists for a type. `Into<T>` is most commonly implemented by
22 taking advantage of the reflexive blanket implentation using `From`, but can
23 also be manually implemented as desired.
24 
25 `.into()` cannot be used in intermediate expressions, because it is impossible
26 for the compiler’s type engine to select a unique `Into<T>` implementation. This
27 means that expressions like `v.into().use()` will never compile. Users can
28 replace `.into()` with `.conv::<Dest>()` in order to inform the compiler of the
29 type of the expression after the conversion, and make compilation succeed.
30 
31 `Conv` cannot be used in trait bounds, because the trait itself is not generic.
32 All `Sized` types implement `Conv` by default, so specifying that a type must be
33 `Conv` adds no information to the solver.
34 
35 # Examples
36 
37 ## Conversion as methods
38 
39 Conversion with `.into()` will fail to compile, even with the type annotation:
40 
41 ```rust,compile_fail
42 let s: String = "static".into().clone();
43 //              ^^^^^^^^^^^^^^^ cannot infer type for `T`
44 // note: type must be known at this point
45 ```
46 
47 while the equivalent code with `.conv::<_>` does compile:
48 
49 ```rust
50 # use wyz::conv::Conv;
51 let s = "static".conv::<String>().clone();
52 ```
53 
54 ## Conversion as traits
55 
56 Bounding a type with `Conv` will not compile, because the trait itself gives no
57 information:
58 
59 ```rust,compile_fail
60 # use wyz::conv::Conv;
61 fn lift<T: Conv>(src: T) -> String {
62   src.conv::<String>().clone()
63 //    ^^^^ the trait `From<T>` is not implemented for `String`
64 // help: consider adding a `where String: From<T>` bound
65 // note: required because of the requirements on the impl of `Into<String>` for `T`
66 }
67 ```
68 
69 This can be fixed by adding the clause `where String: From<T>`, or by using the
70 bound `Into`:
71 
72 ```rust
73 # use wyz::conv::Conv;
74 fn lift<T: Into<String>>(src: T) -> String {
75   src.conv::<String>().clone()
76 }
77 ```
78 
79 The `Into<T>` trait bound makes available both the `Into::<T>::into` method and
80 the `Conv::conv::<T>` method.
81 
82 [`From`]: https://doc.rust-lang.org/std/convert/trait.From.html
83 [`Into`]: https://doc.rust-lang.org/std/convert/trait.Into.html
84 **/
85 pub trait Conv: Sized {
86 	/// Converts `self` into a target type.
87 	///
88 	/// This method runs `<Self as Into<T>>::into` on `self` to produce the
89 	/// desired output. The only difference between using `Conv::conv` and
90 	/// `Into::into` is where the target type is placed in the name; `.conv()`
91 	/// can be used in intermediate positions of an expression, while `.into()`
92 	/// cannot.
93 	///
94 	/// # Examples
95 	///
96 	/// ```rust
97 	/// use wyz::conv::Conv;
98 	///
99 	/// let t = "hello".conv::<String>();
100 	/// ```
conv<T: Sized>(self) -> T where Self: Into<T>101 	fn conv<T: Sized>(self) -> T
102 	where Self: Into<T> {
103 		<Self as Into<T>>::into(self)
104 	}
105 }
106 
107 impl<T: Sized> Conv for T {
108 }
109 
110 /** Directed Fallible Type Conversion
111 
112 This trait is an accessory to [`TryFrom`] and [`TryInto`]. It works by moving
113 the destination type from the trait name (`TryInto<Target>::try_into`) into the
114 method name (`TryConv::try_conv::<Target>`). This change makes `TryInto<_>` the
115 correct trait to use in trait bounds and `.try_conv::<_>` the correct method to
116 use in expressions.
117 
118 A `try_conv::<T>` method is automatically available whenever a `TryInto<T>`
119 implementation exists for a type. `TryInto<T>` is most commonly implemented by
120 taking advantage of the reflexive blanket implentation using `TryFrom`, but can
121 also be manually implemented as desired.
122 
123 `.try_into()` cannot be used in intermediate expressions, because it is
124 impossible for the compiler’s type engine to select a unique `TryInto<T>`
125 implementation. This means that expressions like `v.try_into().use()` will never
126 compile. Users can replace `.try_into()` with `.try_conv::<Dest>()` in order to
127 inform the compiler of the type of the expression after the conversion, and make
128 compilation succeed.
129 
130 `TryConv` cannot be used in trait bounds, because the trait itself is not
131 generic. All `Sized` types implement `TryConv` by default, so specifying that a
132 type must be `TryConv` adds no information to the solver.
133 
134 # Examples
135 
136 ## Conversion as methods
137 
138 Conversion with `.try_into()` will fail to compile, even with the type
139 annotation:
140 
141 ```rust,ignore
142 let s: String = "static".try_into().unwrap().clone();
143 //              ^^^^^^^^^^^^^^^^^^^ cannot infer type for `T`
144 // note: type must be known at this point
145 ```
146 
147 while the equivalent code with `.try_conv::<_>` does compile:
148 
149 ```rust
150 # use wyz::conv::TryConv;
151 let s = "static".try_conv::<String>().unwrap().clone();
152 ```
153 
154 ## Conversion as traits
155 
156 Bounding a type with `TryConv` will not compile, because the trait itself gives
157 no information:
158 
159 ```rust,ignore
160 # use wyz::conv::TryConv;
161 fn lift<T: TryConv>(src: T) -> String {
162   src.try_conv::<String>().clone()
163 //    ^^^^^^^^ the trait `From<T>` is not implemented for `String`
164 // help: consider adding a `where String: From<T>` bound
165 // note: required because of the requirements on the impl of `Into<String>` for `T`
166 // note: required because of the requirements on the impl of `TryFrom<T>` for `String`
167 }
168 ```
169 
170 This can be fixed by adding the clause `where String: TryFrom<T>`, or by using
171 the bound `TryInto`:
172 
173 ```rust
174 # use std::convert::TryInto;
175 # use wyz::conv::TryConv;
176 fn lift<T: TryInto<String>>(src: T) -> String {
177   src.try_conv::<String>().ok().unwrap().clone()
178 }
179 ```
180 
181 The `TryInto<T>` trait bound makes available both the `TryInto::<T>::try_into`
182 method and the `TryConv::try_conv::<T>` method.
183 
184 [`TryFrom`]: https://doc.rust-lang.org/std/convert/trait.TryFrom.html
185 [`TryInto`]: https://doc.rust-lang.org/std/convert/trait.TryInto.html
186 **/
187 pub trait TryConv: Sized {
188 	/// Attempts to convert `self` into a target type.
189 	///
190 	/// This method runs `<Self as TryInto<T>>::try_into` on `self` to produce
191 	/// the desired output. The only difference between using
192 	/// `TryConv::try_conv` and `TryInto::try_into` is where the target type is
193 	/// placed in the name; `.try_conv()` can be used in intermediate positions
194 	/// of an expression, while `.try_into()` cannot.
195 	///
196 	/// # Examples
197 	///
198 	/// ```rust
199 	/// use wyz::conv::TryConv;
200 	///
201 	/// let t = "hello".try_conv::<String>().unwrap();
202 	/// ```
try_conv<T: Sized>(self) -> Result<T, Self::Error> where Self: TryInto<T>203 	fn try_conv<T: Sized>(self) -> Result<T, Self::Error>
204 	where Self: TryInto<T> {
205 		<Self as TryInto<T>>::try_into(self)
206 	}
207 }
208 
209 impl<T: Sized> TryConv for T {
210 }
211