You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently, the Zig compiler does not fail when type aliases are used interchangeably, and I argue it should. I will motivate my proposal with a concrete example.
In the code below, although OwnerId and PetId are technically the same thing (both are u64), they semantically represent different things (an owner and a pet) which are not interchangeable. Functions that accept OwnerId should not be called with PetId and vice-versa, even though the underlying parameter types are perfectly compatible.
Currently, the Zig compiler sees no issue with this, and will compile without errors or warnings.
// You can copy this code as-is and compile it with Zig 0.11.0 with no issues.conststd=@import("std");
constOwnerId=u64;
constPetId=u64;
pubfngetMostPopularPet() PetId {
// Get the most famous pet somehowreturn0;
}
pubfngetPetIdByOwnerId(owner_id: OwnerId) PetId {
// Fetch the PetId by OwnerId somehowstd.debug.print("owner_id: {?}\n", .{owner_id});
return0;
}
pubfnmain() !void {
constfamous_pet: PetId=getMostPopularPet();
// getPetIdByOwnerId is called with a PetId, even though it expects an OwnerId.// This is an application logic error that could be enforced by robust type aliases.constpet_id=getPetIdByOwnerId(famous_pet);
std.debug.print("pet_id: {?}\n", .{pet_id});
}
My proposal is to make the implicit cast of famous_pet to OwnerId raise a compilation error like so
'famous_pet' is a PetId but 'getPetIdByOwnerId' expects a OwnerId. If this is really your intention, explicitly cast it with @as(OwnerId, famous_pet).
In case the user really knows what they are doing and are OK with passing a PetId into an OwnerId, they should tell the compiler of their intentions with a @as(OwnerId, famous_pet). This cast would result in a no-op and the code would work as normal.
I believe this addition would be especially useful for large applications with lots of different entities (users, accounts, repositories, teams, you name it) which relate to each other and make these kinds of mistakes more likely.
The text was updated successfully, but these errors were encountered:
serramatutu
changed the title
Proposal: Require explicit cast between type aliases even if they are compatible
Proposal: Require explicit cast between different type aliases even if they are compatible
Feb 19, 2024
Type aliases in Zig are far more frequently used as just that: aliases. You'll see things like const ArrayList = std.ArrayList, const Build = std.Build, etc dotted all throughout the compiler, standard library, and third-party code. This pattern is encouraged; it makes code more readable.
What you really seem to want is some method to create distinct types, as in the proposal linked above (#1595). When you're working with integers, there is a convention for this today: using a non-exhuastive enum.
constMyId=enum(u64) {
// This marks the enum as "nonexhuastive", so that every `u64` can be used as an// enum variant, even if we don't have an explicit name for it._,
};
This solves the problem for the most common class of type (note that enums are guaranteed to have the same ABI as their backing integer), with the added advantage that it can have declarations (including functions) and explicit tags for special values (e.g. I could define none = std.math.maxInt(u64) to have a convenient-to-use "null"-esque value). If you have a use case not satisfied by this, I'd suggest posting it in #1595 instead.
Currently, the Zig compiler does not fail when type aliases are used interchangeably, and I argue it should. I will motivate my proposal with a concrete example.
In the code below, although
OwnerId
andPetId
are technically the same thing (both areu64
), they semantically represent different things (an owner and a pet) which are not interchangeable. Functions that acceptOwnerId
should not be called withPetId
and vice-versa, even though the underlying parameter types are perfectly compatible.Currently, the Zig compiler sees no issue with this, and will compile without errors or warnings.
My proposal is to make the implicit cast of
famous_pet
toOwnerId
raise a compilation error like soIn case the user really knows what they are doing and are OK with passing a
PetId
into anOwnerId
, they should tell the compiler of their intentions with a@as(OwnerId, famous_pet)
. This cast would result in a no-op and the code would work as normal.I believe this addition would be especially useful for large applications with lots of different entities (users, accounts, repositories, teams, you name it) which relate to each other and make these kinds of mistakes more likely.
The text was updated successfully, but these errors were encountered: