Right now renaming a type and re-exporting it via a type alias can be a breaking change whereas re exporting it under the new name is not a breaking change. As far as I know the only difference between the two is how they interact with tuple-struct constructors:
pub struct MyStruct(u32);
pub use self::MyStruct as PubUse;
pub type PubType = MyStruct;
fn main() {
let _ = PubUse(5); // OK
// let _ = PubType(5); // Doesn't work
}
This happens because tuple struct constructors are just free functions with the same ident as the type they're constructing. use statements bring in items from all 3 namespaces that match the given ident but type items only alias the type itself.
This can be fixed by making it so that the compiler generates a constructor for the alias when it sees that the RHS of the type alias is a tuple-struct. This would be a breaking change because users can already define a free function with the same name as the type alias, which people do currently do to add the necessary constructor to work around this very issue.
To resolve this @joshtriplett has recommended the following steps:
It it possible there's anyone relying on this for the opposite, exporting a type while intentionally not exporting the constructor? (And _not_ providing a function of the same name.)
I guess it doesn't hide PubType { 0: 5 }, though...
Maybe a clippy rule suggesting to use pub use instead of pub type for trivial tuple struct reexports would help people avoid these pitfalls in the current edition?
@scottmcm If someone is relying on that, they could avoid exporting the tuple struct's fields, which makes the constructor private.
Doesn't pub type PubType = MyStruct; differ in semantics to the re-export if there are obligations introduced (or not included) in the type? We don't currently perform many checks on PubType or MyStruct until usage (which is why adding WF-check to types is a breaking change because if PubType isn't used but not WF we would start breaking builds).
Based on discussion in the lang team meeting today:
We'd like to take this through the new lang team process. (That should just involve moving this issue to the rust-lang/lang-team repository using the major change process template.)
This may potentially interact with name resolution. One challenge is that the name resolution code doesn鈥檛 (today) have to look at the RHS of a type alias. cc @petrochenkov
One common use case for this involves aliases that fill in specific type parameters for generic types:
struct Foo<T>(T);
type Bar = Foo<u32>;
The result doesn't currently work as a constructor (Bar(42)), or as a pattern.
This won't just interact with name resolution, this is almost entirely a name resolution feature.
It is possible to do this without heroic efforts, but with significant limitations (same limitations as imports of type-relative associated items).
type Alias = Something; will have to always reserve (possibly unusable) slots in both type and value namespaces regardless of the constructor actually existing or not.
So
type A = u8;
fn A() {}
will become ill-formed.
Most helpful comment
It it possible there's anyone relying on this for the opposite, exporting a type while intentionally not exporting the constructor? (And _not_ providing a function of the same name.)
I guess it doesn't hide
PubType { 0: 5 }, though...