Magnus Hoff
Magnus Hoff

Reputation: 22089

Using generic trait methods like .into() when type inference is impossible

I'm hoping to be able to use .into() to convert a value in a context where type inference is impossible. This is typically when I want to convert a temporary value into some other type for passing it into a generic function. See the following code for an example (playground):

use std::convert::*;

struct NewType(pub i32);

impl From<NewType> for i32 {
    fn from(src: NewType) -> i32 {
        src.0
    }
}

fn main() {
    let a = NewType(5);
    println!("{}", a.into()); // Understandably won't compile
}

I get the error:

error[E0282]: type annotations needed
  --> src/main.rs:13:20
   |
13 |     println!("{}", a.into());
   |                    ^^^^^^^^ cannot infer type for `T`

How do I properly tell the compiler that I want to convert a into i32?

I can get it to work right by explicitly feeding Into with type arguments: Into::<i32>::into(a). This is more verbose and explicit than I was hoping to be able to achieve, especially in a context where I have not imported Into (std::convert::Into::<i32>::into(a)). a.into::<i32>() would be acceptable, but that is not where the type arguments go.

a.into() as i32 would look nice, but this exact syntax doesn't work.

Is there a trick I am missing?

Upvotes: 63

Views: 34321

Answers (6)

dim_voly
dim_voly

Reputation: 541

Not clear which way that experimental ascribing feature is currently going, so here's a trait based method that plays nicer with chained method calls.

To recap, the plain vanilla one liner is as follows:

Into::<i32>::into(a)

With the code below it'll instead be:

use IntoT;
// ...
a.into_t::<i32>()

For that to work, we define a trait extension:

pub trait IntoT {
    fn into_t<T>(self) -> T
    where
        Self: Into<T>;
}

And do a blanket impl for all types:

impl<F> IntoT for F {
    fn into_t<T>(self) -> T
    where
        F: Into<T>,
    {
        self.into()
    }
}

Now you can put the type you're wanting directly into the call.

Upvotes: 1

Mike Mestnik
Mike Mestnik

Reputation: 323

You can hide a function definition away somewhere to get the rest of the code to look nice.

use std::convert::*;

struct NewType(pub i32);

impl From<NewType> for i32 {
    fn from(src: NewType) -> i32 {
        src.0
    }
}

fn main() {
    let a = NewType(5);
    println!("{}", a.get_i32());
}

mod r#impl {
    impl super::NewType {
        pub fn get_i32(self) -> i32 {
            self.into()
        }
    }
}

Upvotes: -2

Lukas Kalbertodt
Lukas Kalbertodt

Reputation: 88576

I don't think there is a better way. Since the type parameter is on the trait, not the method into(), the turbofish operator into::<i32>() doesn't work. As you said, you can make it work by using the fully-qualified-syntax:

Into::<i32>::into(a)

Note, that Into is reexported in std::prelude, which means that you never have to specify the full path, as the trait is always in scope.

Of course, there is also always the possibility to bind your temporary to a name and use the type annotation of the let-binding:

let tmp: i32 = a.into();

It might be better in the future, though! There is an Type Ascription for Expressions RFC, which was already accepted and implemented. The feature is still unstable, but if it's implemented you could write something like:

println!("{}", (a.into(): i32));   // still unstable :/

Upvotes: 50

Magnus Hoff
Magnus Hoff

Reputation: 22089

Apparently, this is possible on Rust nightly with type ascription, which seems to be a feature designed for this use case (playground):

#![feature(type_ascription)]

use std::convert::*;

struct NewType(pub i32);

impl From<NewType> for i32 {
    fn from(src: NewType) -> i32 {
        src.0
    }
}

fn main() {
    let a = NewType(5);
    println!("{}", a.into(): i32);
}

Since this is available in an experimental feature, it might be reasonable to conclude that it is otherwise missing from the language proper.

Upvotes: 9

Simon Kraemer
Simon Kraemer

Reputation: 5680

You could use From::from:

use std::convert::*;

struct NewType(pub i32);

impl From<NewType> for i32 {
    fn from(src: NewType) -> i32 {
        src.0
    }
}

fn main() {
    let a = NewType(5);
    println!("{}", i32::from(a));
}

You can read more about it in the docs for the convert module.

Upvotes: 56

Matthieu M.
Matthieu M.

Reputation: 299790

You can simply annotate the type of the result by assigning it to a variable.

let b: i32 = a.into();
println!("{}", b);

Upvotes: 2

Related Questions