I have a generic struct with a lot of type parameters:
struct J<A, B, C, D, E, F, G>{...}
as well as other structs that use subsets of those parameters:
struct H<A, B, C>{...}
struct I<C, D, E, F, G>{...}
I am writing methods for J
which will often use H
or I
with the same type parameters:
impl<A, B, C, D, E, F,G> J<A, B, C, D, E, F,G> {
fn f_1() -> I<C, D, E, F, G> {...}
fn f_2() -> H<A, B, C> {...}
}
Now re-writing the type parameters so much is very cumbersome, so it only makes sense to be able to do something like:
impl<A, B, C, D, E, F,G> J<A, B, C, D, E, F,G> {
type MyI = I<C, D, E, F, G>;
type MyH = H<A, B, C>;
fn f_1() -> MyI {...}
fn f_2() -> MyH {...}
}
Like you would be able to with associated types in a trait. I haven't found any way of expressing this though that the Rust compiler is happy with. The above errors with:
error[E0658]: inherent associated types are unstable
--> src/lib.rs:14:5
|
14 | type MyI = I<C, D, E, F, G>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #8995 <https://github.com/rust-lang/rust/issues/8995> for more information
error[E0658]: inherent associated types are unstable
--> src/lib.rs:15:5
|
15 | type MyH = H<A, B, C>;
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #8995 <https://github.com/rust-lang/rust/issues/8995> for more information
error[E0223]: ambiguous associated type
--> src/lib.rs:17:17
|
17 | fn f_1() -> Self::MyI {
| ^^^^^^^^^ help: use fully-qualified syntax: `<J<A, B, C, D, E, F, G> as Trait>::MyI`
error[E0223]: ambiguous associated type
--> src/lib.rs:20:17
|
20 | fn f_2() -> Self::MyH {
| ^^^^^^^^^ help: use fully-qualified syntax: `<J<A, B, C, D, E, F, G> as Trait>::MyH`
Is there really no way to do it?
CodePudding user response:
As the compiler error suggests, this functionality is currently not supported. The tracking issue is https://github.com/rust-lang/rust/issues/8995.
As a work-around, you can define a separate trait MyTypes
implemented by J
that has the necessary associated types. However, the syntax for accessing them (<Self as MyTypes>::MyI
) isn't much less verbose. An advantage of this approach is that if you ever need to change MyI
, you will only need to do so in a single place.
trait MyTypes {
type MyI;
type MyH;
}
impl<A, B, C, D, E, F, G> MyTypes for J<A, B, C, D, E, F, G> {
type MyI = I<C, D, E, F, G>;
type MyH = H<A, B, C>;
}
impl<A, B, C, D, E, F, G> J<A, B, C, D, E, F, G> {
fn f_1() -> <Self as MyTypes>::MyI { todo!() }
fn f_2() -> <Self as MyTypes>::MyH { todo!() }
}