Zig, Self, and This

While reading Crafting Interpreters I came upon:

To get at that instance, it needs a name. Smalltalk, Ruby, and Swift use “self”. Simula, C++, Java, and others use “this”.

Zig:

const Self = @This();

:joy_cat: :lizard:

8 Likes

related:

1 Like

Can’t unsee.

But it also has neither “self”/“this”, because the lowercase version refers to something that gives you the instance (not the type) and is just the parameter name in Zig, that has type @This(), that way it is similar to python. (That there isn’t a special keyword/identifier that gets resolved to the instance)

3 Likes

One is style, one is syntax.

I agree with Mr. Cro’s article, always have: I don’t use a pronoun when I’m defining a noun, unless the language forces me to. So member functions are never (self: Self), always (widget: Widget).

The idea of doing it this way (pronoun-oriented) is connected to inheritance in object-oriented programming in the class-hierarchical style which became fashionable in the 90s, but even there, I don’t like it. If Cat isa Animal or whatever, then Animal methods can use animal and Cat methods can use cat. This is better! Imagine you’re debugging, and you step into what you think is a Cat method, and now your receiver is an animal. You learned something. If the signature is self: Self, that isn’t going to help you out.

The convention started with a language called, you guessed it, Self, which is an important language historically. Since it uses prototypes, you genuinely don’t know what thing a method is operating on sometimes, so it’s at least understandable. Weird that we’ve been cargo-culting it for decades in languages which don’t actually work that way, but hey, welcome to programming computers.

Zig, thankfully, doesn’t do inheritance, so it makes even less sense here.

2 Likes

I agree with Mr. Cro’s article, always have: I don’t use a pronoun when I’m defining a noun, unless the language forces me to. So member functions are never (self: Self), always (widget: Widget).

I’m halfway there, with (self: Widget). I don’t do self: Self unless its required (Anonymous Structs and Files as Structs). I find that having the convention of self being the name of a parameter in functions that takes itself as a receiver (even if that is just syntactic sugar in zig) is helpful for understanding the code as I read and writ it.

But I also have done a lot of python programming so it might just be that it’s what i’m familiar with too.

2 Likes

I always use self: Widget but I think you make a strong case and I’m going to rethink that!

1 Like

My main reason for using self: Self in the past was in file-based structs where if I decided I wanted Lexer.zig instead of Scanner.zig, it would spare me from having to do the rename in the code itself. But I realize that doing the rename is a trivial task in any editor, so now I feel that having the added clarity of lexer: Lexer is worth the “trouble” of having to maybe perform that rename some day.

1 Like

I think the python programming is most of it, I’ve also done quite a bit of it but it’s about my #3 language in terms of employment and quite a bit lower in terms of enjoyment.

But the somewhat-implicit nature of member functions in Zig, where the only way to tell them apart is by the type of the first argument, does make a case for that variable always having the same name.

I come down on the other side of that style question, but I do see how that’s a reasonable case. I know the name of the struct when I’m inside the code, so seeing it doubled up in lowercase and TypeCase makes it adequately clear what’s going on.

1 Like

I once used me. Call me evil :slight_smile:

pub const SwapchainImageAcquireResult = struct {
    image_index: u32,
    suboptimal: bool,
};
pub fn acquireSwapchainImage(me: @This(), swapchain: vk.SwapchainKHR, semaphore: vk.Semaphore) !SwapchainImageAcquireResult {
    const acquire = try me.fns.acquireNextImageKHR(me.handle, swapchain, default_timeout, semaphore, .null_handle);
    if (acquire.result == .timeout) return error.VulkanAcquireSwapchainImageTimeout;
    return .{
        .image_index = acquire.image_index,
        .suboptimal = (acquire.result == .suboptimal_khr),
    };
}
2 Likes