Why there is no conversion trait, that is 1) generic over a single type parameter, e.g. `T` and 2) with a single method with the following signature: `fn form(&self) -> T;`

As title, specifically, consider this code snippet that defines a trait known as Form<T>:

trait Form<T>
where
    T: ?Sized,
{
    fn form(&self) -> T;
}

Also, consider this blanket implementation:

use std::borrow::{Borrow, ToOwned};

impl<T, U> Form<T> for U
where
    T: ToOwned<Owned = T>,
    U: Borrow<T>,
{
    fn form(&self) -> T {
        <T as ToOwned>::to_owned(<U as Borrow<T>>::borrow(self))
    }
}

I do think perhaps we should have this trait in the standard library but I don't understand why we don't.

Summary

Basically, a type T that implements Form<U> is saying the following:

A value of type T has sufficient information to 'create' / 'form' a value of type U if it implements Form<U>.

Advantages of this Form<T>

Use Cases

I can imagine these use cases.

  1. We have Configurations struct that we can use to create Executor and whatever other structs.
  2. Different crates can implement Form<StructDefinedInMyCrate> for StructDefinedInOtherCrates, in which StructDefinedInMyCrate is a local type and StructDefinedInOtherCrates is a foreign type.

Comparing with ToOwned

  1. ToOwned is not generic, that is given a type T, by implementing ToOwned<Owned = U>, it means given a &T, only a U can be gotten from it. However, sometimes, we may want &T to be capable of producing more than U value.

Comparing to AsRef<T> (as AsMut<T>)

  1. Form<T> can be seen as a restriction of AsRef<T> whereby AsRef<T> returns a &T, Form<T> is restricting it to return an owned T. Similarly for AsMut<T>.

Comparing to Into<T> (and From<U> where U: Into<T>)

  1. The From and Into traits are equivalent-conversion traits as their methods consume self. Form<T> does not consume self.

Comparing to Borrow<T>

  1. Form<T> can be seen as a restricted (since an owned value is more restrictive than a referred value) version of Borrow<T>. Where impl Borrow<T> for U is roughly saying you can get a &T cheaply from a U, impl Form<T> for U is saying you can get an owned T 'cheaply' from a U.

Hence, the question is:

Why is a trait like Form<T> not in the standard library?

Thank you.

It's very simple, really: you can either have this implementation or existing “conversion from T to T is always allowed” one.

And what we have today is more useful.

Are you sure anyone would want all these advantages after they would lose the ability to pass i32 where From<i32> is expected?

Are you saying Form<T> would conflict with Into<T>?

No, it would conflict with the simplest possible implementation.

Trait resolver is Rust is not advanced enough to understand that T: Borrow<T> is impossible thus to have that rule you would need to eliminate From<T> for T rule.

And that one is much more useful.

Edit: actually T: Borrow<T> is always true, so this would genuinely conflict with any owned type, so enhancement in resolver wouldn't help.

What's wrong with implementing From<&U> for T and therefore Into<T> for &U, though?

6 Likes

Bikeshed, but the name is very prone to From/Form mixup which has already influenced this thread, I think. How about Create<T>?

1 Like

Actually this is good, lol.