Thanks for all the help so far!
When calling into
one has to define the expected type. I tried to use the Turbofish syntaxt, but Rust did not like it. Did I do it incorrectly (the commented out code) or is it only available in the long form at the end of the example?
And if this is the case, why?
fn main() {
let a = "Hello World!";
println!("{a}");
let b = String::from(a);
println!("{b}");
let c: String = a.into();
println!("{c}");
// let d = a.into::<String>();
// method takes 0 generic arguments but 1 generic argument was supplied
// println!("{d}");
let e = Into::<String>::into(a);
println!("{e}");
}
Because you are putting the generic param in the wrong place. It's Into<T>::into
, not Into::into<T>
.
It's completely logical, too. If it were the way you wanted, that would mean that once a type implements the (single, non-generic, hypothetical) Into
trait, it could be turned into any other type whatsoever! That clearly shouldn't even occur to you as something that could be possible.
Into
could be defined as
trait Into: Sized {
fn into<T: From<Self>>(self) -> T {
T::from(self)
}
}
impl Into for T where T: Sized {}
and indeed you can write that (and call it To::to
or whatever) if you want a turbofishable into
. Std’s Into
doesn’t look like that, at least due to the need to sometimes impl Into
manually, without a corresponding From
– I think it’s rare these days but used to be more common before the orphan rules were relaxed.
The problem with that is exactly the From<Self>
bound – while the blanket impl is useful and pervasive, the definition above is not equivalent with the current definition, and is strictly more restrictive.
(Not to mention the conceptual difference – IMO even if the bound makes this possible, the non-generic Into
trait would be the wrong way to model the relationship.)
There's tap::Conv
blanket-implemented for all (Sized
) types, by the way, which works exactly like this.
2 Likes