As I understand it, in Zig const Self = @This() is just a normal constant declaration where Self is the name of the constant. Self is used by convention, but itâs not special like in other languages, so you could use whatever identifier you want. In your code, you have Self declared twice in overlapping scopes so it is indeed ambiguous (although I would have expected a shadowing error instead, maybe someone with more knowledge can explain that.) Although not as convenient, you can use @This() directly too:
Note I had to change the s1 arg in method m2 to type *S1 to get the code to compile. Like in this case, if youâre not going to modify the arguments in the methods you could remove the pointers * to them, using constant arguments instead:
My understanding is exactly the same - I just âmechanicallyâ used Self = @This() and wrongly thought it might work since the scopes are different though nested.
Yes, this seems to be correct - if there was some function that takes both S1 and S2, using Self (or *Self) as a type for both arguments this would make the ambiguity more clear.
Why not using the name of a type directly instead (as in my second example)? What is the benefit of using @This?
But what are those benefits of aliasing type name with Self by means of @This()? Self is just a user defined named and, as @dude_the_builder noted, using this name for alias is a âcommon practiceâ (however this common practice does not work with nested structs). Maybe it is a sort of homage to OOP languages?
If you later have to rename the struct, say from S1 to S2, you donât have to go and replace all the references to S1 in your file because you aliased S1 to Self once and then used Self everywhere else.
In Zig, an imported file is treated as a struct, allowing you to define your structs neatly as separate files. Within that file though, you wonât have a struct name given you donât have an explicit const Name = struct {... statement, so in that case @This is your only alternative to reference the struct itself.
Since you might have a linked list of u8 or a linked list of BigHonkinStructThatRequires1KbOfMemory, how do you declare a pointer to one? You canât use LinkedList since thatâs the function name, so ⌠@This(). But typing that gets old right around the time you hit the âiâ on your keyboard. Thus, an alias is used: Self. Self aligns nicely with some other languages, and it somewhat intuitive when you see self: *Self as a parameter.
Okay, lets look at fn SinglyLinkedList from lib/std/linked_list. The returned structure contains nested structure (Node) with a couple of âmethodsâ. And this nested struct does not uses Self = @This(), because it is impossible (youâll get ambiguous reference).
pub fn insertAfter(node: *Node, new_node: *Node) void {
// Self = @This() idiom works only once, at the highest level.
As I said before, it is a sort of courtesy to OOP languages
But, please, do not get me wrong - I am not criticizing (I am not languages/compilers designer at all), I am just learning Zig. As to Zig approach to generics: if they are impossible without @This compiler intrinsic - well, thatâs nice, no problem.
One additional upside of using @This() directly (not assigning to Self) is avoiding the problems with nested definitions. That and less boilerplate, I guess.
Self and @This are not mutually exclusive. You may assign the result if the latter to something (not necessarily to Self, it can be any name), or you may not⌠depends on a situation. Self is just âtraditionalâ alias for the name of a structure, inside which it is used.
As far as I could understand from the discussion, the only case when we must use @This() is implementing generic data types. Is this true? Are there any other cases/situations where @This() is unavoidable?
if you nest a struct definition in a fn body or a test, you canât use the struct identifier directly, so you have to use @This() (or a const alias to @This())