Name aliases for struct fields and enum variants

Rust has a few useful features for making backwards-compatible API changes — type aliases, pub use, default method implementations in traits, and simply ability for inherent methods to forward calls to another method, and all of that can be shoved under #[doc(hidden)]. Very nice!

However, there's nothing like this for public field names. If I want to rename a field of a struct, I have to make it a breaking change, and can't forward old name to the new one.

There's also nothing reliable for renaming enum variants. It can be partially hacked with an associated constant, but that works only for fieldless enums, and isn't compatible with wildcard imports.

I won't mention any syntax here to delay this bikeshed, but would the functionality make sense in general?

I assume it'd be mostly a syntax-level thing that replaces one name with another as soon as possible in the compilation pipeline.

4 Likes

For fields, at least, it opens some interesting things around borrow splitting or construction or destructuring. Is let Foo { a, b } = foo; legal if they're both actually the same field, for example?

Can you elaborate on the scenario where you have public fields that need renaming? I've always just considered this part of the tradeoff of public fields, assuming people would use a method instead of they want freedom to do things like rename.

1 Like

The rgb crate exports GrayAlpha(T, T) tuple type, and .0 and .1 are not great, and even worse considering that there could be an alpha-first type added that flips meaning of the fields.

I'd like to migrate that to something like LumaAlpha { l: T, a: T }, but since the rgb crate is used for interoperability between crates, making a new incompatible type has a high cost. If there were field aliases, I could do pub type GrayAlpha<T> = LumaAlpha<T> + alias the fields, so that both old gray.0 code and the new luma.l code would continue to work, and keep being interoperable.


let Foo { a, b } = foo;

I imagine it would be a syntactic find'n'replace early, so it'd parse as let Foo { alias_of_a: a, alias_of_b: b } = foo;, and err if the replacement makes it let Foo { same: a, same: b } = foo;. Just like type alias doesn't create new types, and doesn't change how type-related features work.

4 Likes

Syntax could be quite "rust"-like for struct:

struct Point {horizontal: i32, vertical: i32}

type Point.x = Point {horizontal: x, ..}
type Point.y = Point {vertical: y, ..}

You can hack partial syntax compatibility together by having one type Deref to the other via pointer cast. This is how nalgebra enables field access syntax for their vector types despite

Just aliasing on a per-field-identifier basis is much simpler, but the generalized form is a form of safe union that allows fields to arbitrarily alias each other.

For destructuring that names a field twice with different names, I'd expect that to be an identical result to if the field gets named twice by the same identifier.

3 Likes

Not exactly related, but similar: providing a better path for migrating. Because theoretically this could be somewhat automated. especially for simple things like renaming.

Kotlins Deprecated annotation (Deprecated - Kotlin Programming Language) has a ReplaceWith. And theoretically you could write a migrate tool, which just replaces the usage with the new one.

But this requires knowing when the breaking change is happening in an earlier version.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.