Convert Trait
Published on 2018, Dec 2
Create a partner trait to From
that allows conversion in a method chain. Available in the tap
crate now.
- Feature Name: convert_trait
- Start Date: 2018-12-02
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)
Summary
This is a partner trait to From
and Into
that allows using a conversion
method in the middle of a chain.
Motivation
Currently, Rust code cannot write
struct A; struct B; struct C;
impl From<A> for B { /* … */ }
let a: A = A::new();
let c: C = a.into().b_to_c();
This is because the expression a.into()
does not specify a final type, and
there is an infinite set of possible types that may have a method with the
signature fn b_to_c(self) -> C
. The compiler is incapable of determining which
specific Into<?> for A
impl to use, and fails compilation.
Because the Into
trait defines fn into(self) -> T
as a non-generic
function, the expression .into::<T>()
is ill-formed, and attempts to select a
method that does not exist.
Currently, the only way to write this expression without temporaries is to use UFCS:
let c: C = <A as Into<B>>::into(a).b_to_c();
or to use the companion trait, From
:
let c: C = <B as From<A>>::from(a).b_to_c();
Both of these require significantly rearranging the expression, using a leading type and function instead of method and trailing type, and introduce a lot of noise. This loses all the advantages of method syntax, both syntactic and semantic.
This trait supports the use case of type conversions in a method chain where
map
is unavailable. The expected outcome is that a.into().use()
chains will
require a much smaller insertion of type information, rather than a large
rewrite, making writing these expressions more smooth.
Guide-level explanation
Rust offers the std::convert
module as a defined set of interfaces for
converting values from one type to another. The From
trait is the idiomatic
standard for moving values between types, and the companion traits Into
and
Convert
provide automatic conveniences for invoking it.
A type conversion between an existing source type, Source
, and a target type,
Target
, is established by writing an impl From<Source> for Target
block with
the conversion function. Once this block exists, the universal implementations
in the standard library make Into<Target>
and Convert
methods available on
Source
.
Conversion from an instance of the source type to the target type can be
achieved with the From
free function or with the Into
or Convert
methods.
#[derive(Clone, Copy)]
struct Source;
struct Target;
impl From<Source> for Target {
fn from(src: Source) -> Self {
Target
}
}
fn main() {
let a = Source;
let b = Target::from(a);
let c: Target = a.into();
let d = a.convert::<Target>();
}
Into
and Convert
are complements of each other. Into<Target>
is the right
choice for use in trait bounds, while .convert::<Target>()
is the right choice
for use in method calls. They both perform the same underlying thing:
<Target as From<Self>>::from(self)
.
Reference-level explanation
In the std::convert
module, define a new trait:
pub trait Convert : Sized {
fn convert<T: Sized + From<Self>>(self) -> T {
<T as From<Self>>::from(self)
}
}
and blanket-implement it on all sized types:
impl<T: Sized> Convert for T {}
Drawbacks
Why should we not do this?
The standard library should not be a slowly-accumulating pile of idioms.
Rationale and alternatives
-
Why is this design the best in the space of possible designs?
It's a trivial wrap around
From
, just like howInto
is written -
What other designs have been considered and what is the rationale for not choosing them?
Type ascription : apparently difficult, has not made much progress, requires language-level support
-
What is the impact of not doing this?
Method chains are less ergonomic than possible when they require type conversions.
Prior art
The Into
trait shows willingness in the standard library to define companion
traits that function only as a reshaping of how the base trait is invoked.
Unresolved questions
-
What parts of the design do you expect to resolve through the RFC process before this gets merged?
What should it be named?
-
Convert
is the full word -
Conv
is four letters, matchingFrom
andInto
- other?
-
-
What parts of the design do you expect to resolve through the implementation of this feature before stabilization?
None
-
What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC?
None
Future possibilities
None