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
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*! Directed Type Conversion

This module provides sibling traits to the `std::convert` module. The standard
library puts the type parameter in the trait declaration, which makes those
traits generic and suitable for constraint clauses and function calls, but not
usable in indeterminate method-call positions. These traits put the type
parameter in the function declaration, making the trait non-generic and allowing
the function to be called in method-call position without ambiguity.
!*/

use core::convert::TryInto;

/** Directed Type Conversion

This trait is an accessory to [`From`] and [`Into`]. It works by moving the
destination type from the trait name (`Into<Target>::into`) into the method name
(`Conv::conv::<Target>`). This change makes `Into<_>` the correct trait to use
in trait bounds and `.conv::<_>` the correct method to use in expressions.

A `conv::<T>` method is automatically available whenever an `Into<T>`
implementation exists for a type. `Into<T>` is most commonly implemented by
taking advantage of the reflexive blanket implentation using `From`, but can
also be manually implemented as desired.

`.into()` cannot be used in intermediate expressions, because it is impossible
for the compiler’s type engine to select a unique `Into<T>` implementation. This
means that expressions like `v.into().use()` will never compile. Users can
replace `.into()` with `.conv::<Dest>()` in order to inform the compiler of the
type of the expression after the conversion, and make compilation succeed.

`Conv` cannot be used in trait bounds, because the trait itself is not generic.
All `Sized` types implement `Conv` by default, so specifying that a type must be
`Conv` adds no information to the solver.

# Examples

## Conversion as methods

Conversion with `.into()` will fail to compile, even with the type annotation:

```rust,compile_fail
let s: String = "static".into().clone();
//              ^^^^^^^^^^^^^^^ cannot infer type for `T`
// note: type must be known at this point
```

while the equivalent code with `.conv::<_>` does compile:

```rust
# use wyz::conv::Conv;
let s = "static".conv::<String>().clone();
```

## Conversion as traits

Bounding a type with `Conv` will not compile, because the trait itself gives no
information:

```rust,compile_fail
# use wyz::conv::Conv;
fn lift<T: Conv>(src: T) -> String {
  src.conv::<String>().clone()
//    ^^^^ the trait `From<T>` is not implemented for `String`
// help: consider adding a `where String: From<T>` bound
// note: required because of the requirements on the impl of `Into<String>` for `T`
}
```

This can be fixed by adding the clause `where String: From<T>`, or by using the
bound `Into`:

```rust
# use wyz::conv::Conv;
fn lift<T: Into<String>>(src: T) -> String {
  src.conv::<String>().clone()
}
```

The `Into<T>` trait bound makes available both the `Into::<T>::into` method and
the `Conv::conv::<T>` method.

[`From`]: https://doc.rust-lang.org/std/convert/trait.From.html
[`Into`]: https://doc.rust-lang.org/std/convert/trait.Into.html
**/
pub trait Conv: Sized {
	/// Converts `self` into a target type.
	///
	/// This method runs `<Self as Into<T>>::into` on `self` to produce the
	/// desired output. The only difference between using `Conv::conv` and
	/// `Into::into` is where the target type is placed in the name; `.conv()`
	/// can be used in intermediate positions of an expression, while `.into()`
	/// cannot.
	///
	/// # Examples
	///
	/// ```rust
	/// use wyz::conv::Conv;
	///
	/// let t = "hello".conv::<String>();
	/// ```
	fn conv<T: Sized>(self) -> T
	where Self: Into<T> {
		<Self as Into<T>>::into(self)
	}
}

impl<T: Sized> Conv for T {
}

/** Directed Fallible Type Conversion

This trait is an accessory to [`TryFrom`] and [`TryInto`]. It works by moving
the destination type from the trait name (`TryInto<Target>::try_into`) into the
method name (`TryConv::try_conv::<Target>`). This change makes `TryInto<_>` the
correct trait to use in trait bounds and `.try_conv::<_>` the correct method to
use in expressions.

A `try_conv::<T>` method is automatically available whenever a `TryInto<T>`
implementation exists for a type. `TryInto<T>` is most commonly implemented by
taking advantage of the reflexive blanket implentation using `TryFrom`, but can
also be manually implemented as desired.

`.try_into()` cannot be used in intermediate expressions, because it is
impossible for the compiler’s type engine to select a unique `TryInto<T>`
implementation. This means that expressions like `v.try_into().use()` will never
compile. Users can replace `.try_into()` with `.try_conv::<Dest>()` in order to
inform the compiler of the type of the expression after the conversion, and make
compilation succeed.

`TryConv` cannot be used in trait bounds, because the trait itself is not
generic. All `Sized` types implement `TryConv` by default, so specifying that a
type must be `TryConv` adds no information to the solver.

# Examples

## Conversion as methods

Conversion with `.try_into()` will fail to compile, even with the type
annotation:

```rust,ignore
let s: String = "static".try_into().unwrap().clone();
//              ^^^^^^^^^^^^^^^^^^^ cannot infer type for `T`
// note: type must be known at this point
```

while the equivalent code with `.try_conv::<_>` does compile:

```rust
# use wyz::conv::TryConv;
let s = "static".try_conv::<String>().unwrap().clone();
```

## Conversion as traits

Bounding a type with `TryConv` will not compile, because the trait itself gives
no information:

```rust,ignore
# use wyz::conv::TryConv;
fn lift<T: TryConv>(src: T) -> String {
  src.try_conv::<String>().clone()
//    ^^^^^^^^ the trait `From<T>` is not implemented for `String`
// help: consider adding a `where String: From<T>` bound
// note: required because of the requirements on the impl of `Into<String>` for `T`
// note: required because of the requirements on the impl of `TryFrom<T>` for `String`
}
```

This can be fixed by adding the clause `where String: TryFrom<T>`, or by using
the bound `TryInto`:

```rust
# use std::convert::TryInto;
# use wyz::conv::TryConv;
fn lift<T: TryInto<String>>(src: T) -> String {
  src.try_conv::<String>().ok().unwrap().clone()
}
```

The `TryInto<T>` trait bound makes available both the `TryInto::<T>::try_into`
method and the `TryConv::try_conv::<T>` method.

[`TryFrom`]: https://doc.rust-lang.org/std/convert/trait.TryFrom.html
[`TryInto`]: https://doc.rust-lang.org/std/convert/trait.TryInto.html
**/
pub trait TryConv: Sized {
	/// Attempts to convert `self` into a target type.
	///
	/// This method runs `<Self as TryInto<T>>::try_into` on `self` to produce
	/// the desired output. The only difference between using
	/// `TryConv::try_conv` and `TryInto::try_into` is where the target type is
	/// placed in the name; `.try_conv()` can be used in intermediate positions
	/// of an expression, while `.try_into()` cannot.
	///
	/// # Examples
	///
	/// ```rust
	/// use wyz::conv::TryConv;
	///
	/// let t = "hello".try_conv::<String>().unwrap();
	/// ```
	fn try_conv<T: Sized>(self) -> Result<T, Self::Error>
	where Self: TryInto<T> {
		<Self as TryInto<T>>::try_into(self)
	}
}

impl<T: Sized> TryConv for T {
}