Skip to content

Commit b395325

Browse files
committed
Update with improved method names, From trait
1 parent d269a9c commit b395325

File tree

1 file changed

+72
-31
lines changed

1 file changed

+72
-31
lines changed

text/0000-conversion-traits.md

+72-31
Original file line numberDiff line numberDiff line change
@@ -110,27 +110,36 @@ might expect: we introduce a total of *four* traits:
110110

111111
```rust
112112
trait As<Sized? T> for Sized? {
113-
fn cvt_as(&self) -> &T;
113+
fn convert_as(&self) -> &T;
114114
}
115115

116116
trait AsMut<Sized? T> for Sized? {
117-
fn cvt_as_mut(&mut self) -> &mut T;
117+
fn convert_as_mut(&mut self) -> &mut T;
118118
}
119119

120120
trait To<T> for Sized? {
121-
fn cvt_to(&self) -> T;
121+
fn convert_to(&self) -> T;
122122
}
123123

124124
trait Into<T> {
125-
fn cvt_into(self) -> T;
125+
fn convert_into(self) -> T;
126+
}
127+
128+
trait From<T> {
129+
fn from(T) -> Self;
126130
}
127131
```
128132

129-
These traits mirror our `as`/`to`/`into` conventions, but add a bit
130-
more structure to them: `as`-style conversions are from references to
131-
references, `to`-style conversions are from references to arbitrary
132-
types, and `into`-style conversions are between arbitrary types
133-
(consuming their argument).
133+
The first three traits mirror our `as`/`to`/`into` conventions, but
134+
add a bit more structure to them: `as`-style conversions are from
135+
references to references, `to`-style conversions are from references
136+
to arbitrary types, and `into`-style conversions are between arbitrary
137+
types (consuming their argument).
138+
139+
The final trait, `From`, mimics the `from` constructors. Unlike the
140+
other traits, its method is not prefixed with `convert`. This is
141+
because, again unlike the other traits, this trait is expected to
142+
outright replace most custom `from` constructors. See below.
134143

135144
**Why the reference restrictions?**
136145

@@ -140,7 +149,7 @@ would have to use generalized where clauses and explicit lifetimes even for simp
140149
```rust
141150
// Possible alternative:
142151
trait As<T> {
143-
fn cvt_as(self) -> T;
152+
fn convert_as(self) -> T;
144153
}
145154

146155
// But then you get this:
@@ -150,17 +159,19 @@ fn take_as<'a, T>(t: &'a T) where &'a T: As<&'a MyType>;
150159
fn take_as<T>(t: &T) where T: As<MyType>;
151160
```
152161

153-
What's worse, if you need a conversion that works over any lifetime,
154-
*there's no way to specify it*: you can't write something like
162+
If you need a conversion that works over any lifetime, you need to use
163+
higher-ranked trait bounds:
155164

156165
```rust
157166
... where for<'a> &'a T: As<&'a MyType>
158167
```
159168

160169
This case is particularly important when you cannot name a lifetime in
161170
advance, because it will be created on the stack within the
162-
function. While such a `where` clause can likely be added in the
163-
future, it's a bit of a gamble to pin conversion traits on it today.
171+
function. It might be possible to add sugar so that `where &T:
172+
As<&MyType>` expands to the above automatically, but such an elision
173+
might have other problems, and in any case it would preclude writing
174+
direct bounds like `fn foo<P: AsPath>`.
164175

165176
The proposed trait definition essentially *bakes in* the needed
166177
lifetime connection, capturing the most common mode of use for
@@ -176,6 +187,14 @@ cost and consumption, and having multiple traits makes it possible to
176187
(by convention) restrict attention to e.g. "free" `as`-style conversions
177188
by bounding only by `As`.
178189

190+
Why have both `Into` and `From`? There are a few reasons:
191+
192+
* Coherence issues: the order of the types is significant, so `From`
193+
allows extensibility in some cases that `Into` does not.
194+
195+
* To match with existing conventions around conversions and
196+
constructors (in particular, replacing many `from` constructors).
197+
179198
## Blanket `impl`s
180199

181200
Given the above trait design, there are a few straightforward blanket
@@ -184,53 +203,60 @@ Given the above trait design, there are a few straightforward blanket
184203
```rust
185204
// As implies To
186205
impl<'a, Sized? T, Sized? U> To<&'a U> for &'a T where T: As<U> {
187-
fn cvt_to(&self) -> &'a U {
188-
self.cvt_as()
206+
fn convert_to(&self) -> &'a U {
207+
self.convert_as()
189208
}
190209
}
191210

192211
// To implies Into
193212
impl<'a, T, U> Into<U> for &'a T where T: To<U> {
194-
fn cvt_into(self) -> U {
195-
self.cvt_to()
213+
fn convert_into(self) -> U {
214+
self.convert_to()
196215
}
197216
}
198217

199218
// AsMut implies Into
200219
impl<'a, T, U> Into<&'a mut U> for &'a mut T where T: AsMut<U> {
201-
fn cvt_into(self) -> &'a mut U {
202-
self.cvt_as_mut()
220+
fn convert_into(self) -> &'a mut U {
221+
self.convert_as_mut()
203222
}
204223
}
224+
225+
// Into implies From
226+
impl<T, U> From<T> for U where T: Into<U> {
227+
fn from(t: T) -> U { t.cvt_into() }
228+
}
205229
```
206230

231+
The interaction between
232+
207233
## An example
208234

209235
Using all of the above, here are some example `impl`s and their use:
210236

211237
```rust
212238
impl As<str> for String {
213-
fn cvt_as(&self) -> &str {
239+
fn convert_as(&self) -> &str {
214240
self.as_slice()
215241
}
216242
}
217243
impl As<[u8]> for String {
218-
fn cvt_as(&self) -> &[u8] {
244+
fn convert_as(&self) -> &[u8] {
219245
self.as_bytes()
220246
}
221247
}
222248

223249
impl Into<Vec<u8>> for String {
224-
fn cvt_into(self) -> Vec<u8> {
250+
fn convert_into(self) -> Vec<u8> {
225251
self.into_bytes()
226252
}
227253
}
228254

229255
fn main() {
230256
let a = format!("hello");
231-
let b: &[u8] = a.cvt_as();
232-
let c: &str = a.cvt_as();
233-
let d: Vec<u8> = a.cvt_into();
257+
let b: &[u8] = a.convert_as();
258+
let c: &str = a.convert_as();
259+
let d: Vec<u8> = a.convert_into();
234260
}
235261
```
236262

@@ -242,7 +268,7 @@ impl Path {
242268
fn join_path_inner(&self, p: &Path) -> PathBuf { ... }
243269
244270
pub fn join_path<P: As<Path>>(&self, p: &P) -> PathBuf {
245-
self.join_path_inner(p.cvt_as())
271+
self.join_path_inner(p.convert_as())
246272
}
247273
}
248274
```
@@ -295,16 +321,19 @@ So a rough, preliminary convention would be the following:
295321
in this RFC. An *ad hoc conversion trait* is a trait providing an ad
296322
hoc conversion method.
297323

298-
* Use ad hoc conversion methods for "natural" conversions that should
299-
have easy names and good discoverability. A conversion is "natural"
300-
if you'd call it directly on the type in normal code; "unnatural"
301-
conversions usually come from generic programming.
324+
* Use ad hoc conversion methods for "natural", *outgoing* conversions
325+
that should have easy method names and good discoverability. A
326+
conversion is "natural" if you'd call it directly on the type in
327+
normal code; "unnatural" conversions usually come from generic
328+
programming.
302329

303330
For example, `to_string` is a natural conversion for `str`, while
304331
`into_string` is not; but the latter is sometimes useful in a
305332
generic context -- and that's what the generic conversion traits can
306333
help with.
307334

335+
* On the other hand, favor `From` for all conversion constructors.
336+
308337
* Introduce ad hoc conversion *traits* if you need to provide a
309338
blanket `impl` of an ad hoc conversion method, or need special
310339
functionality. For example, `to_string` needs a trait so that every
@@ -323,6 +352,18 @@ So a rough, preliminary convention would be the following:
323352
* Use the "inner function" pattern mentioned above to avoid code
324353
bloat.
325354

355+
## Prelude changes
356+
357+
*All* of the conversion traits are added to the prelude. There are two
358+
reasons for doing so:
359+
360+
* For `As`/`To`/`Into`, the reasoning is similar to the inclusion of
361+
`PartialEq` and friends: they are expected to appear ubiquitously as
362+
bounds.
363+
364+
* For `From`, bounds are somewhat less common but the use of the
365+
`from` constructor is expected to be rather widespread.
366+
326367
# Drawbacks
327368

328369
There are a few drawbacks to the design as proposed:

0 commit comments

Comments
 (0)