This example seemed to stop just short of the really interesting bit: what if other.zig called Tea.drink()? Would it set Tea.full to false? Maybe this is obvious to Zig users, but coming from C++, that would be a violation of const correctness.
In C++ (thinking of classes rather than files), you wouldn't be able to call the drink method of a const object because it's not marked as a const method. Or, if it was marked as a const method, you wouldn't be able to modify full from in drink. You could get around this by marking full as mutable, which means you're going to deliberately violate const correctnees, but at least you have to be explicit about it. (In theory mutable is meant for things like caches that don't affect the visible behaviour of the class.)
To be clear: you would first need to create an instance of `Tea`.
var t: Tea = .{};
t.drink(); // will work and set t.full to false
If instead we declared `t` as `const`, then yes the call to `drink` would not have been possible.
`const Self = @This();` just binds the top-level struct type definition to a name, which then allows you to refer to it in other plances. Nothing more than that.
In this example, `foo` and `bar` cannot modify `self`, while `baz` can. In the case of `foo`, `self` is passed in by value, but since function arguments are immutable in Zig, it cannot be modified (the compiler might still opt for pass-by-reference under the hood, but the semantics don't change).
In the case of `bar` we are explicitly asking for a constant pointer, as you mentioned.
I spent a lot of this weekend learning Zig, and this was the most surprising this for me. foo, bar, and baz are all called the same way (as thing.foo() or thing.baz()), but depending on the type signature, the compiler can figure out whether you want a mutable or immutable reference to the object.
It’s pretty common for languages in the field e.g. C++ (const versus non-const methods), Rust (value, unique reference, shared reference, deref), and even Go (value v pointer, delegation).
The “method syntax” is pretty much just a convenience, so why not also handle that after all?
Interesting, thanks. In that case, what if `Tea` was not const? Could I assign a different type to it? What would the static type of an instance of Tea be, if its runtime value could be two incompatible types?
1. You can use a type variable as a normal one, as long as you do it at compile time (a variable of type type is required to be comptime). You can use a TypeInfo at runtime if you like though.
2. The type system is deep enough to describe the type of values, but not the type of types. That's what higher kinded types are for, and for better or for worse that's not part of zig. Also, type variables can only exist at compile time, so there is no runtime value.
In C++ (thinking of classes rather than files), you wouldn't be able to call the drink method of a const object because it's not marked as a const method. Or, if it was marked as a const method, you wouldn't be able to modify full from in drink. You could get around this by marking full as mutable, which means you're going to deliberately violate const correctnees, but at least you have to be explicit about it. (In theory mutable is meant for things like caches that don't affect the visible behaviour of the class.)