>for loop can only be used to iterate over elements of an array, if you want to just execute a block N times, you need while…
The Zig docs suggest something like this:
var i: usize = 0;
while (i < 10) {
i += 1;
}
Is there a plan to provide a mechanism to stop "i" from remaining in scope after the end of the loop? I couldn't find anything relevant with a quick web search.I use integer iterating for-loops a lot in C. I just checked a 5000 line project of mine that deals with bitmaps. There are 100 occurrences of something like:
for (int i = 0; i < 10; i++) {
I think these would all become while-loops in Zig. Exposing 100 variables to a larger scope than they need to be sounds like a source of bugs to me. {
var i: usize = 0;
while (i < 10) : (i += 1) {
// use i
}
} for ([1]u0{0} ** 10) |_, i| {
std.debug.print("{d}", .{i});
}If you're doing it a lot you can abstract it into a function to clean it up:
fn count(comptime n: usize) [n]u0 {
return [1]u0{0} ** n;
}
for (count(10)) |_, i| {
std.debug.print("{d}", .{i});
}
or, if you don't need to capture the index: for (count(9)) |_| {
//I can do it 9 times
}
Personally I've found I rarely need this kind of loop, so I just use the 'while' formulation because it is more obvious.I will be amused when std.math.range shows up with this implementation.
{ var i: usize = 0; while (i != 10) : (i += 1) {
...
}}
I.e., you are not obliged to plaster on a newline after every semicolon. That is roughly as busy as the equivalent C++ loop, for (auto i = 0u; i != 10; ++i) {
...
}
but syntax is syntax. You would be much better off with a function that takes a lambda, and pass the body as that lambda. In C++, a loop using that would look like forloop(10u, [&](auto i) {
...
});
That's not a much shorter than the built-in, but leaves a lot less room for mistakes; i.e., if there was a typo in the built-in, it might be very hard to make yourself see it even knowing it must be there.I can't find anything to suggest Zig has lambdas. That's a planet-sized hole, nowadays.
(BTW, forloop in C++ is just
template <typename Ix, typename Body>
void forloop(Ix top, Body&& body) {
for (Ix i{}; i != top; ++i) body(i);
}
In use it compiles to instructions identical to the built-in loop.)Closures are problematic in low-level languages, as their capture behaviour requires tricky syntax and/or implicitness, and Zig is very much opposed to both. Non-capturing anonymous functions expressions will be added (https://github.com/ziglang/zig/issues/1717), while a limited form of closure is being considered (https://github.com/ziglang/zig/issues/6965).
repeat(10) {
println("Counter is $it")
}
Kotlin optimizes nicely for the most default case while giving you flexibility if you need it (different parameter name, step, etc...).If you just think about this specific issue, then it seems obvious that the solution should be to change the syntax to accommodate what other C-like languages are doing, but if you look at the language as a whole will see all the other problems that get introduced, making this change less straight forward than it might seem initially.
My recommendation to the people that want to see this changed is to do the work to understand enough about Zig to be able to produce a proposal and see how that goes. There even are ~regular meetings where a guest can participate to present a proposal to the language design committee.
That said, here's my take (just to clarify, I'm not part of the language design committee): I think it's fine as is and the solution is to just use blocks the right way.
In other comments people showed the example of encapsulating a while loop in a block, but that's just an artificial example that I agree doesn't seem too convincing. The reality is that leaking a variable is not terribly bad per se, it only becomes bad when a function has a lot of things going on and also maybe multiple loops, like you mentioned here:
> Exposing 100 variables to a larger scope than they need to be sounds like a source of bugs to me.
My argument at this point is that if you have a function with multiple while loops and enough things going on that leaking a variable would be a problem, then you should have blocks in place already, which in other words means that it's not the while loop that needs to be artificially wrapped in a block to overcome a language limitation, but rather that the loop + its surrounding context should be wrapped in a very natural block regardless.
Of course having the ability to use a while loop to restrict the lifetime of its index would be better, but what I mentioned just now about wrapping each context makes me think that it's not that bad of a problem and, on the other hand, the design problem I mentioned in the beginning makes me think, given what I know currently, that this might very well be a sweetspot despite the "common sense" complaints that people have.
Warnings in Zig are called 'compiler errors'. Which is to say, Zig doesn't do warnings. If the compiler thinks there is a problem, you must fix it. Usually you've made a mistake, but sometimes you just need to be explicit about what you want to happen. In this particular case I believe there is an issue raised about it, but I couldn't find it on github.
> Zig float literals have type comptime_float, which is essentially the largest type, ie. f128. To use a different type you have to cast it first and I’m not loving the syntax: [...]
> It is also a bit inconsistent, as some functions (like pow) will take a type parameter and allow for comptime params: [...]
Yes, this is an inconsistency in the standard library that hasn't yet been addressed as Zig currently doesn't place a high priority on polishing the standard library.
Andrew Kelley, Zig's designer, presents Zig as an alternative to C because it can replace C where it is used, but even though C served as a conceptual starting point in the language's design process, comparing the language itself to C (or "C+"), as opposed to C++, doesn't quite capture the language's design and feel.
Superficially, what makes Zig feel "slim" compared to C++ is that 1. it disallows any kind of name overloading (and, so, in particular operator overloading) -- any (namespaced) name in a Zig program can refer to only one definition -- and 2. it is a very simple language that can be fully picked up in a day or two by anyone with experience in any low-level language (C, C++, Rust, or Ada). But Zig is both safer and more expressive than C++; any "rich" C++ construct that makes clever use of, say, templates, can be expressed in Zig and more easily, thanks to Zig's comptime and introspection, and much more. It is no coincidence that programs are short in comparison to other low-level languages. It's not that the language is terse, but that its simple constructs are powerful, similar to how Lisps can be simple yet expressive, although Zig's comptime + introspection replaces Lisp's macros as the "power feature" (and comptime + introspection is easier to read and write than macros). So while the language shares some basic primitives with all low-level languages, like precise control over data representation, it doesn't feel like any other language.
I see Zig as an entirely new, and quite revolutionary approach to low-level programming, that doesn't follow any design tradition in that space, neither C's sparseness nor C++/Ada/Rust's linguistic richness. It is something new altogether.
Different languages appeal to different people based on their aesthetic preferences and priorities (e.g. C++ and Rust prioritise the code appearing as if written in a high-level language when read on the page, while Zig prioritises explicitness and compilation times), and surely some developers would prefer C++ or Rust to Zig, just as surely as others would prefer Zig, but to understand what Zig is, it has to be experienced, because it really is hard to compare to anything else. Thankfully, being so simple and easy to learn, experiencing it requires very little investment. You may find it a promising prospect for any low-level programming task, regardless of whether it would have otherwise suited C or C++. It can replace C++ just as much as C, and even C++ developers would find it compares very favourably when it comes to expressive power.