Yes, but in f1 your return type is T whereas in f2 it is i32. That both are the same in your example is just a coincidence. Imagine you implement X for f32 as well and your user calls f2::<f32>(). He won't get a value of f32, but of i32, which is not T in that case.

You could make f2 compile if you were to use an abstract return type instead. That way your user can't control T, but you, the person implementing f2, can choose it freely to be i32 (see here):

The trick is what rustc can prove:
In the first example, it can prove that the type of T::Y is the same as T.

In the second example however, there is a problem: f2 promises that for any type T, it will return a value of T.
Yet what you return is a hard i32 value. What happens if we implement the trait for String as well and call f2::<String>()?