Live data from Hacker News

Failing to Learn Zig via Advent of Code

forrestthewoods.com

301–310 of 338 posts

Re: Failing to Learn Zig via Advent of Code

#301
post #293

Earlier quoted context omitted.

If dealing with potentially hostile data, Zig certainly isn't more appropriate than Rust in my opinion, try maybe WUFFS. Suppose we have been given a 32KB data structure with some "step" bytes - in a conforming input these should always sum to less then 32768 and thus the total will easily fit in a 16-bit unsigned integer, so that's what our naive program does. Unfortunately attackers provided a structure whose step…

> If dealing with potentially hostile data, Zig certainly isn't more appropriate than Rust in my opinion, try maybe WUFFS. Thanks! Great recommendation on WUFFS! And completely agreed, it's also easy to turn on checked arithmetic for Rust (if you know about it, but Rust definitely has an unsafe default there for those that don't, which is surprising to me). At the same time, WUFFS is not always applicable, for exampl…

Something like WUFFS is exactly what we should be using for Wrangling Untrusted File Formats as it says in the name, even if you've decided to do that in a distributed system. Realistically you're definitely going to get this wrong, so, use a language where the worst case is it doesn't work is a massive improvement over using languages where it's all additional attack surface.

That recent Apple bug where they render PNGs incorrectly can (in principle) happen in WUFFS. The other recent Apple bug where bad guys seize control of your iPhone by sending a malicious image file cannot. One of these things is not like the other.

I think you're missing the point if you expect the borrow checker to care about buffer underflow. Rust has a runtime bounds check to check bounds, the borrow checker is, as its name suggests, checking the borrow rules. The trick (compared to arithmetic overflow) is that the optimiser can often push a bounds check outside a fast loop or eliminate it altogether, so you really can afford to do this in all or almost all your release code unlike checked arithmetic. WUFFS shows that you can do away with both of these runtime checks and be entirely safe if you're not interested in being a general purpose programming language. Which is (part of) why WUFFS gets to be both safer and faster. Both Zig and Rust are intended as general purpose languages.

I don't buy the "surfaces the bug" thing because I have too much experience of real world systems where there's so much noise and mayhem that you are focused on stuff that's causing your real users pain. Even if the DoS means the server falls over and must be manually restarted, the ticket in my queue says "Urgent: Auto-restart server. Watchdog maybe?" not "OMG bad guys are trying to break into our system somehow, find out how ASAP"

Re: Failing to Learn Zig via Advent of Code

#302
post #213

Earlier quoted context omitted.

Why does something as basic as uppercasing a string or decoding latin1 require a third-party library? I would expect that to be part of stdlib in any language. Also, why does that third-party library come with its own string implementation? What if my dependency X uses zigstr but dependency Y prefers zig-string https://github.com/JakubSzark/zig-string >? Basically all languages designed in the past 30 years have at l…

That's not "simple". Rust also does neither of those two tasks with just the stdlib! - latin1 is dead and should be in no stdlib in 2022 - uppercasing requires the current Unicode tables, so, a largish moving target that you probably don't want to embed in small programs.

Latin-1 is actually the first 256 code points from Unicode. So, you can do that in Rust by casting u8 (the Latin-1 bytes) into char (Unicode scalar values). That's unintuitive perhaps because of course in C that wouldn't do anything useful since the char type isn't Unicode, but in Rust that's exactly what you wanted.

In this environment you might very well not need actual uppercase/ lowercase but only the ASCII subset. Accordingly Rust provides that too, which is far less to carry around than the Unicode case rules. Since the ASCII case change can always be performed in situ (if you can modify the data) Rust provides that too if it's what you want.

Re: Failing to Learn Zig via Advent of Code

#303
post #233

Earlier quoted context omitted.

So it sounds like Zig makes a distinction between pointers and arrays. Am I reading that right?

Yes, arrays are another distinction: - an array [3]u8 - a single item non-nullable pointer *u8 - a single item nullable pointer ?*u8 - a multi-item non-nullable pointer [*]u8 - a multi-item nullable pointer ?[*]u8 - a slice []u8 Typically your API is just made up of slices and non-nullable single item pointers. Arrays are just the typical backing store for a slice, that you might define in main or for small scratch b…

Replying to note I mistakenly left in the `![]u8` return type when it's not returning any byte slice. Should be `!void`.

Re: Failing to Learn Zig via Advent of Code

#304
post #300

Earlier quoted context omitted.

The rounding can depend 'where' (e.g. the width of the register) the float is stored in the CPU ('destination' in IEEE 754 speak). So, if you compare the result stored in two different 'destinations' (because one has been computed in another register or at a GPU or ....), they can differ even in the same program with the same optimizations. See for example: int main() { double q; q = 3.0/7.0; if (q == 3.0/7.0) printf…

I find the argument unconvincing. While IEEE 758 does not specify what `/` (or `+`) operator in particular does (that falls under C standard), it does specify that: > Implementations shall provide the following formatOf general-computational operations, for destinations of > all supported arithmetic formats, and, for each destination format, for operands of all supported arithmetic > formats with the same radix as th…

> But I do think that this is more a quirk on how operators are done specifically in C and not a general matte

That has nothing to do with C, but with the CPU and its registers. x86 has 80bit floating point registers (too), so if the compiler/CPU (doesn't matter which language) saves a value in a 80 bit floating point register and moves it from there to memory where it is stored in 64 bits, the number gets rounded (not truncated, it's actually converted).

See also the GCC page:

    For instance, in the following code segment, depending on the compilation 
    flags and numbers and calculations used to find tmp, the following code may 
    print out that the values are different:


     double tmp, X[2];
     tmp = ....
     tmp += ....
     ...
     X[0] = tmp;
     if (X[0] == tmp)
        printf("Values are the same, as expected!\n");
     else
        printf("Values are different!\n");

    This is because tmp will typically be moved to a register during register 
    assignment, which means tmp may hold a full 80 bits of accuracy, some of 
    which are lost in the store to X[0], and thus the numbers are no longer 
    equal. You may workaround this problem by always explicitly storing to 
    memory to force the round-down.
https://gcc.gnu.org/wiki/x87note

> Are the comparison and arithmetic operations considered to be contracted in these sort of situations?

No. Or well, maybe. 'Contracted' means using e.g. FMA (fused multiply and add), so addition and multiplication like `ab + c` is done in a single step instead of two. Which means that the result is only rounded once (`ab + c` is rounded), whereas without this fusing/contracting `ab` would be rounded and `ab + c` would be rounded again (actually depending on the optimization/compiler flags). So the results (may) differ.

Re: Failing to Learn Zig via Advent of Code

#305
post #280

Earlier quoted context omitted.

> The comptime machinery is really cool, but it gives the compiler much less information to work with for producing good errors. Why do you think that's the case?

Because all the semantics for what are the valid parameters for types and other comptime operations are scattered throughout the code that is run at comptime. It's the same problem as with C++ template code, but perhaps worse. For instance, if you do a formatted print and pass an integer to a "{s}" descriptor, the std.fmt code calls @compileError with an informative message, then a dozen lines of stack trace are prin…

I'm still not quite seeing the problem. To my mind, comptime parameters have a direct translation to parametric type parameters in something like system F or a similar parametrically polymorphic type system, and reasonable type errors are possible in those. Do Zig's comptime parameters universally violate parametricity in some way, and this is why the errors are more difficult?

Re: Failing to Learn Zig via Advent of Code

#306
post #175

Earlier quoted context omitted.

That's a way of reading it

When saying it positively I often say UTSL instead (Use The Source, Luke).

Isnt the problem with that approach you have a model of the implementation (and not of the 'interface')? I mean you dont know if you are useing the function as intended and if not used as intended it might (actually will) break in the future.

For auditing you are right, of course.

Re: Failing to Learn Zig via Advent of Code

#307
post #251
post #158

Earlier quoted context omitted.

Yes, I also had the impression Rust is a C replacement.

Rust's purpose, its whole reason to exist, is to displace C. Rust will unavoidably fail in that, because anybody still using C is not willing to learn anything else: anybody willing to move on from C already did, long ago. Rust is already approaching C++ in complexity, surpassing it in some places; and also in expressive power, but not surpassing it anywhere yet. If Rust does not end up fizzling (which is still very…

Very minor correction - Nim tends to attract people specifically with its language power via its powerful Lisp-like metaprogramming facilities, static introspection, etc. These features are expressly there to automate away mundane repetition. I do not think it belongs in a list with Zig & C the way you use it here.

I also think users of C (not sure about Zig) are quite happy to automate things. Linus Torvalds is a big user of C. He wrote a little C-like compiler to check Linux kernel code called Sparse [1]. You seem to be trying to discuss maybe larger (but not very well articulated) subpopulations of "Users" than Apex Programmers like Linus. It is definitely easier to do this with C than giant languages like C++.

Why, the 1980s & 1990s were littered with maybe dozens of hacked C compilers doing "this or that" automation in a way you do not see for C++ (and will probably never see for Rust). In point of fact, C++ itself (C with classes) was an early example of such! The idea was to automate/codify the object-oriented style of Simula in C.

pjmlp's sibling & child comments are also some good color on the history/context of all this. { Of course, partly it all depends on what you meant by "language power" and "automate" - I am just going by what that seemed like. }

[1] https://sparse.docs.kernel.org/en/latest/

Re: Failing to Learn Zig via Advent of Code

#308
post #185

Earlier quoted context omitted.

Thing is, if Zig is C, why bother at all, we already have C for it.

Zig is warts-free C, but you can use them together. You can gradually refactor your C codebase, Zig can even transpile C. Zig is also a standalone C toolchain, compiling and cross-compiling C is a breeze with it, and it has it's own libc implementation.

it does not have it's own implementation of libc, it just makes easier to work with implementations of libc and letting the cross compile, I think it was like that.

However a libc is a thing that zig probably wants and something I am also thinking on doing if I have the time

Re: Failing to Learn Zig via Advent of Code

#309
post #38

> [Compiling] takes about ~3 seconds minimum which is frustratingly slow I feel old, I know that any time is an opportunity to get distracted but 3 seconds doesn't strike me as a long compile time. Or is that a typo for 30, which would make more sense, which is definitely long enough to be a frustration?

when you don’t have in editor compiler errors and are trying to learn instant feedback can be nice

fun stuff, zig syntax error stuff is handled both by zls and zig fmt --ast-check

(or if you care only for one file) zig ast-check =)

For compile errors I think zig build is the only way

Re: Failing to Learn Zig via Advent of Code

#310
post #250

Earlier quoted context omitted.

Zig's "fancy" (I don't think they're that fancy) type features that IMO make it a great C alternative are: - non-null pointers, and distinct types for single-item pointers and multi-item pointers (multi-item pointers are rarely used except indirectly via slices, so unchecked pointer arithmetic errors are largely banished) - builtin tagged unions (AKA algebraic data types) with very pleasant to use switch logic -- it…

Also Zig's memory alignment in the type system is great for doing low-level I/O. Coming from C, I find that Zig's memory alignment options are easier and more powerful.

Totally agree. Having std.os.mmap enforce correct page_size alignment for the base pointer has saved me once already (using mmap and memfd for a fixed size circular buffer where you can always provide contiguous bytes for the full size available, without any memmove).
Post reply on HN