3
\$\begingroup\$

There are occasionally times when it would be nice to be able to specify a type parameter for Into::into.

This doesn't happen very often—mostly just when you have a long-ish chain of method calls where the object type is changing. In that case, wrapping one part of the chain with T::from, or splitting it into multiple statements so you can specify the type in the variable binding would be kind of awkward.

I put together the following trait:

pub trait TypedInto {
    fn typed_into<T>(self) -> T where Self: Into<T> { self.into() }
}

impl<T> TypedInto for T {}

And I can now specify the type for into:

let clip = clips::table.find(id).first::<db::Clip>(&*conn)?
    .typed_into::<models::Clip>()
    .with_associations(&*conn)?;

Am I going to regret using this? This is in the context of an application rather than a library, so I don't necessarily need to worry about anyone other than myself reading it. Is this a bad habit? Should I just be more verbose?

\$\endgroup\$
  • \$\begingroup\$ One question per question, please. You are welcome to re-post the second half as a separate question. \$\endgroup\$ – 200_success Oct 4 '17 at 17:01
  • \$\begingroup\$ @200_success, the second half wasn't really a second question; it was just more examples for my main question, which is whether "global" convenience functions like these are worth it in Rust. I think that question does belong here and may be specific to Rust; I haven't felt anxiety about doing things like this in other programming languages. My particular implementation of typed_into isn't even especially interesting or relevant. \$\endgroup\$ – kardeiz Oct 4 '17 at 17:20
  • \$\begingroup\$ On Code Review, we have concrete discussions about specific code. If you're looking for a general opinion on the value of global convenience functions, with these code samples serving merely as examples, that's too broad. \$\endgroup\$ – 200_success Oct 4 '17 at 17:31
  • \$\begingroup\$ Why not just do first::<models::Clip> and avoid the transformation? \$\endgroup\$ – Shepmaster Oct 7 '17 at 15:13
  • \$\begingroup\$ @Shepmaster, db::Clip is a struct for use with Diesel where each field maps directly to a database column. models::Clip is a slightly different struct with additional fields (e.g., fields for a record's "associations"). It's just easier with Diesel to map to the DB struct and then expand from there. \$\endgroup\$ – kardeiz Oct 7 '17 at 21:38
1
\$\begingroup\$

The most common way to handle type-hinting Into::into in stable Rust today is via temporaries. So for the use case you have here, I'd probably write something like:

let clip = {
    let clip: models::Clip = clips::table_find(id).first::<db::Clip>(&*conn)?.into();
    clip.with_associations(&*conn)?
};

But honestly, I don't think anyone will get up in arms about you using TypedInto. I know I've typed .into::<Type>() a couple times, and the common place you'd see the turbofish, .collect::<Vec<_>>(), is exactly this.


For this specific case, why don't you make a common db::T -> model::T conversion? It seems to be a common thing that happens, so making it explicit may be beneficial.


But that said, why don't we take a look at nightly feature gates? Because since 2015 the RFC for Type Ascription has been merged. It introduces an expr : ty syntax to the language meant to address cases like this. So rewriting your example to use type ascription,

let clip = ((clip::table.find(id)
    .first(&*conn)? : db::Clip)
    .into() : models::Clip)
    .with_associations(*&conn)?;

Unfortunately, the binding of type ascription, while the same as as, is lower than method call syntax (.), so the parenthesis are necessary (and rustfmt would probably not approve of the formatting I used here). There is a patch written which would cause type ascription to bind more tightly than method calls, but it was closed in favor of waiting to later RFC the change (which would be backwards compatible (probably)).

There has been some recent noise around the feature, since it would make working with Futures a lot easier (though impl Trait may also help as well). Stabilization (since it has been approved through RFC) is blocked on ensuring soundness and allowing &[1, 2, 3] : &[u8] to work, so if you'd love to see type ascription stabilize, you can help with one of those.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.