Rust

Okay, so it's not all sunshine and rainbows...


You can't turbofish Into::into()

I was just writing some code that involves a pretty simple conversion from type A to type C via type B. I initially had some nice-looking code like this:

#![allow(unused)]
fn main() {
struct A;
impl A { fn new() -> Self { Self } }
impl A { fn something(self) -> Self { self } }
impl A { fn something_else(self) -> Self { self } }
struct B;
impl From<A> for B { fn from(_other: A) -> Self { B } }
struct C;
impl From<B> for C { fn from(_other: B) -> Self { C } }
let something: A = A::new();

let x = something
    .something()
    .something_else()
    .into::<B>()
    .into::<C>();
}

However, hitting the play button will reveal an error message. It rightfully tells us that the into() function does not take any generic parameters. Instead, the Into trait itself is generic. This means, if we want to use turbofishing, we'd have to write this:

#![allow(unused)]
fn main() {
struct A;
impl A { fn new() -> Self { Self } }
impl A { fn something(self) -> Self { self } }
impl A { fn something_else(self) -> Self { self } }
struct B;
impl From<A> for B { fn from(_other: A) -> Self { B } }
struct C;
impl From<B> for C { fn from(_other: B) -> Self { C } }
let something: A = A::new();

let x = Into::<C>::into(
    Into::<B>::into(
        something.something().something_else()
    )
);
}

There are two problems with this: first, it looks awful, and second, this is what From is for.

We (and by that, I mean this Reddit comment) can actually do better by writing our own version of Into where the method is generic, then adding a blanket impl over the real Into so that we get existing conversions for free. It looks like this:

#![allow(unused)]
fn main() {
struct A;
impl A { fn new() -> Self { Self } }
impl A { fn something(self) -> Self { self } }
impl A { fn something_else(self) -> Self { self } }
struct B;
impl From<A> for B { fn from(_other: A) -> Self { B } }
struct C;
impl From<B> for C { fn from(_other: B) -> Self { C } }
/// Turbofishable version of [`Into`](Into)
pub(crate) trait IntoT {
    /// Performs the conversion
    fn into_t<T>(self) -> T
    where
        Self: Into<T>;
}

// Blanket impl delegating to `Into`
impl<U> IntoT for U {
    #[inline]
    fn into_t<T>(self) -> T
    where
        Self: Into<T>,
    {
        self.into()
    }
}

let something: A = A::new();

let x = something
    .something()
    .something_else()
    .into_t::<B>()
    .into_t::<C>();
}

And it works too. The downside is that you now have to have this random extra bit of code somewhere in your project and it won't be instantly recognizable by other Rust programmers.

Doctests don't work on private items

Pretty straightforward. You can write doctests on private items but running cargo test won't execute them. "Private items" here meaning anything not exposed in the public API or anything in a bin crate. There's an open issue about this here.

False-positive warnings for dead code in integration tests

Not really sure how I didn't hit this till just today, but either way this is pretty unfortunate. My current workaround is to just keep everything in one massive file, which is not great, but I like the other workarounds even less. Here's the GitHub issue.