This is an awesome post.<p>At the risk of shedding it to bikes, one point that the author makes is that Zig's lack of operator overloading makes him write vector math like this:<p><pre><code> if (discriminant > 0.0) {
// I stared at this monster for a while to ensure I got it right
return uv.sub(n.mul(dt)).mul(ni_over_nt).sub(n.mul(math.sqrt(discriminant)));
}
</code></pre>
He signs off with:<p>> <i>How do C programmers manage?</i><p>The answer is simple: we assign names to intermediate results. Now, I have absolutely no idea what that expression computes, because I suck at graphics programming and math in general. Please pretend that these are proper mathy terms:<p><pre><code> if (discriminant > 0.0) {
const banana = uv.sub(n.mul(dt))
const apple = banana.mul(ni_over_nt)
const pear = n.mul(math.sqrt(discriminant)
return apple.sub(pear)
}
</code></pre>
I'm convinced that there's a proper mathy/lighting-y word for each component in that expression. Of course this approach totally breaks down if you're copying expressions from papers or articles without understanding why they are correct (which is how I do all my graphics programming). I do find that naming variables is often a great way to force myself to grok what's going on.
> But rendering in separate threads turned out to be (unsurprisingly) harder than the way I would do it in C++...It was a bit frustrating to figure out how to accomplish this. Googling yielded a few stack overflow posts with similar questions, and were answered by people basically saying use my crate!<p>Based on some discussion in r/rust (<a href="https://www.reddit.com/r/rust/comments/c7t5za/writing_a_small_ray_tracer_in_rust_and_zig/" rel="nofollow">https://www.reddit.com/r/rust/comments/c7t5za/writing_a_smal...</a>) I went ahead and added a Rayon-based answer to that SO question (<a href="https://stackoverflow.com/a/56840441/823869" rel="nofollow">https://stackoverflow.com/a/56840441/823869</a>). That's been the de facto standard for data parallelism in Rust for the last few years. But the article highlights that discovering the de facto standards is still a challenge for new Rust users -- does anyone know of a well-maintained list of the 10-20 most critical crates that new users should familiarize themselves with after reading The Book? Things like Rayon and lazy_static. The ranked search results at <a href="https://crates.io/crates?sort=recent-downloads" rel="nofollow">https://crates.io/crates?sort=recent-downloads</a> are <i>almost</i> good enough, but they include a lot of transitive dependencies that new users shouldn't care about. (I.e. `regex` is a very important crate, but `aho-corasick` is usually only downloaded as a dependency of `regex`.)
What a delightful post. Author wrote a very nice description of things they learned from a little weekend project. No preaching or ranting or opinionating. Just “I did a thing and here’s what I learned”. That’s easily my favorite type of blog post.<p>Thanks for sharing! ️
> I wrapped my objects in atomic reference counters, and wrapped my pixel buffer in a mutex<p>Rust people, is there a way to tell the compiler that each thread gets its own elements? Do you really have to either (unnecessarily) add a lock or reach for unsafe?
Zig is worth supporting on Patreon: <a href="https://www.patreon.com/andrewrk" rel="nofollow">https://www.patreon.com/andrewrk</a>
I didn't quite grok this bit:<p>> <i>The ability to return values from if expressions and blocks is awesome and I don’t know how I’ve managed to live up until now without it. Instead of conditionally assigning to a bunch of variables it is better to return them from an if expression instead.</i><p>The example shown (aside from the fact it's assigning a tuple, which is a different point) would naturally be a ternary in C/C++. Does the awesomeness kick in for more complicated examples where you want intermediate vars etc in the two branches?
Really cool post. I always dreamed of writing a small realtime, raytraced game, with lights etc. Basically a roguelike in 3D. I never managed to finish it, and this project reminds me of it.
I really don't think these languages should be set opposite to one another as much as they do. I mean, I get why they are. They take up almost the same position and have quite different ways to view security and lang design. And they both try to grow and compete.
But still. I think there are some space for them both.
I would really think it would be cool to spend my working hours programming Rust for safety and switching to Zig whenever I have to code an unsafe block (all of it compiling to webasm :o )