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