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.