- 2 points
- LLMs also generally don't put spaces around em dashes — but a lot of human writers do.
- That's exactly right. There's a really good article about it here: https://www.pagetable.com/?p=39
- Let's throw this into godbolt: https://clang.godbolt.org/z/qW3qx13qT
By themselves, the mod 6 and mod 3 operations are almost identical -- in both cases the compiler used the reciprocal trick to transform the modulo into an imul+add+cmp, the only practical difference being that the %6 has one extra bit shift.is_divisible_by_6(int): test dil, 1 jne .LBB0_1 imul eax, edi, -1431655765 add eax, 715827882 cmp eax, 1431655765 setb al ret .LBB0_1: xor eax, eax ret is_divisible_by_6_optimal(int): imul eax, edi, -1431655765 add eax, 715827882 ror eax cmp eax, 715827883 setb al retBut note the branch in the first function! The original code uses the && operator, which is short-circuiting -- so from the compiler's perspective, perhaps the programmer expects that x % 2 will usually be false, and so we can skip the expensive 3 most of the time. The "suboptimal" version is potentially quite a bit faster in the best case, but also potentially quite a bit slower in the worst case (since that branch could be mispredicted). There's not really a way for the compiler to know which version is "better" without more context, so deferring to "what the programmer wrote" makes sense.
That being said, I don't know that this is really a case of "the compiler knows best" rather than just not having that kind of optimization implemented. If we write 'x % 6 && x % 3', the compiler pointlessly generates both operations. And GCC generates branchless code for 'is_divisible_by_6', which is just worse than 'is_divisible_by_6_optimal' in all cases.
- That's pretty close to escaping the Earth's gravity well, but not quite out, since the Moon is definitely still orbiting the Earth.
- Humans have not left Earth's gravity well. We've built probes that have, but humans have only gotten as far as orbit.
- > On one hand, the fact that the same sound can be named A# and Bb may be puzzling for a kid
I think that, given the toy is (currently) diatonic, and doesn't really have any ability to visualize the chromatic scale (like a piano keyboard does), using the formally correct note names is more intuitive. That way, only the accidentals change when you change modes ("when I change it to C minor, the B becomes a Bb"). This naturally teaches a simple and correct mental model: "the slider chooses a letter and pushing the orange knob makes letters flat or sharp".
If you only ever use sharps instead of sticking to the correct notation, then the notes change inconsistently between different keys ("changing from C major to C minor turns the B into an A#, but changing from C# major to C# minor changes the F into an E"). This is incomprehensible unless you've already memorized the piano keyboard layout.
The OP's choice of restricting to the diatonic scale seems sensible to me -- it helps the kid learn the vocabulary of Western music (if that's your goal!) and it benefits the parents as well by making it hard to create something that sounds bad.
- That is fantastic, I love it!
If I may submit an extremely pedantic music nerd bug report: at 46s in the video demo (https://www.youtube.com/watch?v=qboig3a0YS0&t=46s), the display should read Bb instead of A#, as the key of C minor is written with flats :)
(The precise rule is that a diatonic scale must use each letter name for exactly one note, e.g. you can't have both G and G# in the same key, and you can't skip B. This has many important properties that make music easier to read and reason about, such as allowing written music to specify "all the E's, A's, and B's are flat" once at the start of the piece instead of having to clutter the page with redundant sharps or flats everywhere.)
- If no references are involved, writing unsafe Rust is significantly easier than writing correct C, because the semantics are much clearer and easier to find in the documentation, and there's no insane things like type-based aliasing rules.
If references are involved, Rust becomes harder, because the precise semantics are not decided or documented. The semantics aren't complicated; they're along the lines of "while a reference is live, you can't perform a conflicting access from a pointer or reference not derived from that reference". But there aren't good resources for learning this or clarifying the precise details. This area is an active work-in-progress; there is a subteam of the Rust project led by Ralf Jung (https://www.ralfj.de/blog/) working on fully and clearly defining the language's operational semantics, and they are doing an excellent job of it.
When it comes to Zig, the precise rules and semantics of the memory model are much less clear than C. There's essentially no documentation, and if you search GitHub issues a lot of it is undecided and not actively being worked on. This is completely understandable given Zig's stage in development, but for me "how easy it is to write UB-free code" boils down to "how easy is it to understand the rules and apply them correctly", and so to me Zig is very hard to write correctly if you can't even figure out what "correct" is.
Once Zig and Rust both have their memory models fleshed out, I hope Zig lands somewhere comparable to where Rust-without-references is today, and I hope that Rust-with-references ends up being only a little bit harder (and still easier than C).
- Every reverse engineer learns very quickly that "add [rax], al" has the machine code representation "00 00".
- > There's a "logical" bit order that these operations follow, which starts with the MSBit and ends with the LSBit
Well, normally when bits are numbered, "bit 0" is the least significant bit. The MSB is usually written on the left, (such as for left and right shifts), but that doesn't necessarily make it "first" in my mind.
- CDs use 44.1kHz because your sample rate needs to be double the highest frequency you want to encode to avoid aliasing artifacts: https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampli...
20kHz is the top of the human hearing range, and picking something a little bit higher than 40kHz gives you room to smoothly roll off frequencies above the audible range without needing an extremely steep filter that would create a large phase shift.
- > publicly disclosing these for small resource constrained open source project probably creates a lot more risk than reward.
You can never be sure that you're the only one in the world that has discovered or will discover a vulnerability, especially if the vulnerability can be found by an LLM. If you keep a vulnerability a secret, then you're leaving open a known opportunity for criminals and spying governments to find a zero day, maybe even a decade from now.
For this one in particular: AFAIK, since the codec is enabled by default, anyone who processes a maliciously crafted .mp4 file with ffmpeg is vulnerable. Being an open-source project, ffmpeg has no obligation to provide me secure software or to patch known vulnerabilities. But publicly disclosing those vulnerabilities means that I can take steps to protect myself (such as disabling this obscure niche codec that I'm literally never going to use), without any pressure on ffmpeg to do any work at all. The fact that ffmpeg commits themselves to fixing known vulnerabilities is commendable, and I appreciate them for that, but they're the ones volunteering to do that -- they don't owe it to anyone. Open-source maintainers always have the right to ignore a bug report; it's not an obligation to do work unless they make it one.
Vulnerability research is itself a form of contribution to open source -- a highly specialized and much more expensive form of contribution than contributing code. FFmpeg has a point that companies should be better about funding and contributing to open-source projects that they rely on, but telling security researchers that their highly valuable contribution is not welcome because it's not enough is absurd, and is itself an example of making ridiculous demands for free work from a volunteer in the open-source community. It sends the message that white-hat security research is not welcome, which is a deterrent to future researchers from ethically finding and disclosing vulnerabilities in the future.
As an FFmpeg user, I am better off in a world where Google disclosed this vulnerability -- regardless of whether they, FFmpeg, or anyone else wrote a patch -- because a vulnerability I know about is less dangerous than one I don't know about.
- > it's just silly to draw a a particular sharp line between Rust and Zig because they perform exactly the same (in terms of sound guarantees; we're ignoring any softer effects) for most top weaknesses.
There is a very clear sharp line between them in that Rust has no undefined behavior outside of an unsafe block. This matters because the effects of undefined behavior -- particularly memory-corrupting undefined behavior -- are unbounded. Security issues caused by logic bugs are limited to only having local effects on the program -- a SQL injection bug can lead to the wrong SQL query being executed, a path traversal bug can lead to the wrong path being accessed, and a shell escaping bug can lead to execution of the wrong shell command. These are severe problems that can lead to data exfiltration or remote code execution, but the relationship between bug and effect is straightforward: "I'm passing user input to a shell here; this could have catastrophic consequences so I'd better review this carefully." In contrast, undefined behavior can happen anywhere in your program, and it can do anything. That is a clear and measurable difference, and emperical evidence from the last 10 years clearly indicates both that it is nigh impossible to write large C/C++ applications without both spatial and temporal memory safety vulnerabilities, and that adopting Rust measurably decreases the incidence of such vulnerabilities.
Zig is certainly an improvement over C on this front, but it is still a UB-heavy language, and that's where the sharp line is. It's hard to make emperical comparisons because Zig is so young, but the comparision between Deno and Bun in the article is a reasonably strong demonstration that Zig has not achieved a comparable level of memory safety to Rust.
> Sure, but then you might as well also consider softer effects. For example, maybe a language that's easier to review because it's more explicit, or a language that's faster to compile and is easier to test wins.
> And I agree that we should consider all these, but then we start seeing why correctness is such a complicated topic, and we could speculate just as easily that it is Zig that "clearly" wins.
This doesn't really have anything to do with my point that the CVE list does not provide evidence to dismiss temporal memory safety as irrelevant; it's more of a general statement on writing correct software. But regardless, "Zig has nullability and bounds checks" and "Rust has an affine type system, statically-checked immutability and exclusivity, language-level resource management, and no undefined behavior" are not in the same league of program correctness. Rust wasn't designed after some ideal of memory safety at the expense of clarity and correctness: the lifetime and type system came from a goal of reducing logic bugs in complex concurrent programs, and the fact that it is powerful enough to achieve memory safety without garbage collection was a happy accident. The emperical results that Rust programs have fewer memory-safety vulnerabilities demonstrate that Rust's static approach to software correctness is successful, and not just for the specific problem of memory safety, because Rust's tooling for achieving program correctness is generalizable to arbitrary application invariants. This lines up with my own experience; software I write using Rust is far easier to get right, more reliable, and easier to successfully maintain and refactor than anything else I've done.
Certainly Zig has its strong points as well -- explicitness can reduce complexity and compile times, but it also has downsides of pushing complexity into application code (thus making it harder to review and introducing more opportunities to create mistakes). A proper comparison of the two approaches is worthwhile, but just as "Rust is more memory safe" is an overly reductive generalization of the langauges' approach to software correctness, "there's a rather small difference between Rust and Zig" simply isn't true.
- Limiting to the "top 5" vulnerabilities by number of CVEs feels like cherry-picking. It's true that spatial memory safety is lower-hanging fruit than temporal memory safety, sure; but the CVE list is dominated by vulnerabilities in web applications which are mostly already written in fully memory-safe languages (additionally, these vulnerabilities are typically mitigated by library/API design rather than by programming language design, in which case Rust gives you quite a lot more tools than Zig for designing APIs that have concepts of unsanitized/untrusted data, but I digress). If you filter for vulnerabilities relevant to applications that would typically be written in C/C++/Rust/Zig, use-after-free is easily within the top 5.
The exact positioning within the top 10 is also quite noisy: if you look at last year's list, UAF is in the #1 spot for actively exploited vulnerabilities, even beating out all the web ones: https://cwe.mitre.org/top25/archive/2023/2023_kev_list.html
Lastly, filtering on CVEs has a high selection bias: just because security researchers go for the easy vulnerabilities first doesn't mean that the harder ones can be ignored. As high-profile projects adopt tooling and mitigations to reduce the impact of spatial memory safety problems, it's common to see a sudden increase in CVEs related to temporal memory safety. This is not because use-after-free bugs somehow became more common or severe -- it's because once you eliminate the easy source of vulnerabilities, the attackers shift their attention to the new lowest-hanging fruit.
- > As a small example wrt Rust: resetting an arraylist/vector while reusing its memory is a weirdly complicated trick
A bit of a nitpick here: the trick is for creating a new vector of a different type, that reuses the allocation of an existing vector, using only safe code. "Resetting an arraylist/vector while reusing its memory" is just vec.clear() if you're not changing the type. And it's trivial to change the type of a vector with unsafe (the docs even have examples of this); the only advantage of the "trick" is to ensure you'll get a panic if you screw up the alignment of the types.
And after that blog post, the Rust team accepted a proposal to introduce a vec.recycle() function to change the type of a vector without needing that trick (and with compile-time checks for alignment compatibility): https://github.com/rust-lang/rust/pull/148416
But that example aside, I agree with your overall premise -- Zig and Rust share different design goals here, and Zig's lower level of abstraction makes it more approachable to learn than a language like Rust.
- > I'm not familiar with the guy
Graydon Hoare is the creator of Rust.
So of course he likes Rust. And of course his blog post is going to be comparing Fil-C to Rust. Anyone can write a blog post evaluating Fil-C, but only Graydon can compare it to his own design choices and motivations when he attempted to solve a similar problem. That's the whole thing that makes this post interesting.
- I think that both languages deserve to be evaluated on their technical merits, of which there are many. I would say they are both very well-designed languages that occupy different points in the design space and each have unique innovations of their own, and are worthy of the attention they get. I also think both languages have strong communities and good technical leadership.
What I'm frustrated about is a general pattern in online discourse of treating programming language adoption as a zero-sum game and pitting the communities against each other. This is nothing remotely new -- holy wars over tooling are just about as old as computing itself -- but Rust seems to have become right at the center of it in the last few years, and I think this is likely just a result of it becoming "mainstream" enough for people to care about it outside of a niche circle. I consider that a community issue with Rust, but probably an unavoidable one given the scale of the Internet.
The thing that feels different to me about Zig is that the language's leadership participates in the "holy war", which is different than what I have observed from Rust's leadership. It's one thing if random netizens are trash talking the "opposing team" like a sports fan, but I would expect people who are deeply involved in programming language design and representing their community to know better and behave better than that. It's surprising and disappointing to me; it drags down the overall quality of discourse and sets a bad example for the language communities.
Maybe my own perspective on this is skewed, perhaps I'm looking at Rust's past with rose-tinted glasses. But I think my experiences are comparable. From 2015-2020 I was primarily working with Swift and closely following that language's design and development, and the Rust community (which I was not involved in at all) felt like friendly neighbors. The two communities shared similar goals and had a lot of like-minded people, and because of that there was a lot of overlap and amicability between the communities. Both languages' teams were frequently comparing notes and copying features from one another.
Now I'm primarily working with Rust and closely following that languages' design and development, and it seems to me like the Zig community (which I am not involved with at all) should be on the same friendly terms for the same reasons, but the vibes I see from them are "this town isn't big enough for the two of us", and I'm bothered by that.
> I've looked at the major rust implements from big corp. It's all Arc, copy/clone, and people getting fired.
I have no idea what this is supposed to mean. Are you saying you've seen projects implemented in Rust go poorly? If so, I don't know what to say without any context, besides that I don't think there's a "one size fits all" for programming languages.
Or are you saying big companies' contributions towards the Rust project itself have gone poorly? I don't really know what to say about that either. I have felt that the direction the project has taken over the past few years has represented me and addressed my needs well, so I have confidence in their technical direction and leadership for the time being.
- > people who didn't like Rust flocked to Zig as an alternative, and were keen to promote it as an alternative to Rust by criticising Rust, as wider usage would provide them more of an ecosystem to use in their own Zig programs.
> People who were happy with Rust didn't have same need to criticise Zig in online spaces as Rust is the established player in the C alternatives space. (Though Rust is on the other side when compared to C once you expand the space to "all low level programming languages").
I think that this effect is...more real and intentional than I wish it was or think is healthy.
There's no denying that Rust benefited greatly from heavy "language evangelism" from around 2014-2022 or so (rough subjective range of dates, ending with when I feel like Rust became sufficiently mainstream that "comment section evangalism" is no longer the primary driver of the language's growth). The atmosphere that surrounded the language evangalism during this era -- from my recollection as someone who was aware of Rust but explicitly not interested in it at the time -- felt like one of curiousity and exploring new innovation in the programming language design space, and it didn't come across as pushy or some kind of moral imperative.
Additionally, most of the evangelism was not driven by the Rust project itself, but more of a grassroots thing from:
- Early users
- People who thought the language was interesting without having used it
- The attention that came from prolific early Rust projects like Servo and ripgrep
- The attention that came from the language's technical innovations
(The only person I remember regularly seeing in Rust threads who was actually associated with the project was Steve Klabnik, and I consider him a stellar example of how to represent a project online in a way that's respectful and not pushy or combative.) And eventually I had a programming task where, thanks to those deep, insightful, and respectful comment threads, I recognized that Rust was the right tool for the task I was trying to accomplish, learned the language myself, found it a joy to use, and began using it more regularly.
But once Rust became mainstream, the eternal September kicked in. Now there seem to be quite a lot of people who are more interested in "language wars" than in healthy, technical discussion of programming languages, and it's exhausting. Many threads about C or C++ or Rust or Zig or Fil-C or whatever seems to get overtaken by these obnoxious, repetitive, preachy, ideological comments about why you should or shouldn't be using Rust, often with very ill-informed armchair takes.
I think this is just something that comes from mainstream Internet culture and is not very representative of "the Rust community", and especially not the Rust project -- most of the serious writing and comments coming from people actually involved with Rust have that spirit of respect, openness, and curiousity about programming language design that I remember from the 2010's. A lot of people who work on Rust are deeply interested in the tradeoffs and innovations of new programming languages and fundamentally excited about the possibility of Rust becoming obsolete because something better has come along. But now that Rust adoption is increasing and the broader Internet culture is being forced to consider it, you see a lot of unhealthy reactionary takes from people who act like programming languages are a zero-sum game and they need to pick a side.
Now Zig is where this gets a little bit odd to me. My impression (as an outsider to the Zig community as well) is that Zig's leadership has chosen to view adoption as a zero-sum language war. It's as if they considered:
- How Rust benefited from language evangelism during the 2010's
- How many other "C-replacement" languages over the past 25 years have failed to gain large-scale adoption
- How "language war" discourse is extremely effective for enagement on places like HN
...and decided that the best way to increase Zig adoption is by intentionally leaning into that high-engagement "language war" discourse. Maybe it's not intentional -- and there's certainly a lot of cool ideas and high-quality technical discussion that comes out of the Zig community -- but I've also seen a consistent pattern of snarky or hostile comments from Zig leadership towards Rust people (such as calling Rust users "safety coomers", or jumping into in-depth technical discussions of Rust's downsides like compiler performance with substance-less cheap shots). Whereas I can't remember ever seeing anything but respect and curiosity from people involved in the Rust project towards other communities.
I'm just tired of seeing people draw battle lines around programming languages instead of promoting a spirit of learning from each other and making our tools better.
But note:
> Does it run at the full speed of an original 6502 chip?
> No; it's relatively slow. The MOnSter 6502 runs at about 1/20th the speed of the original, thanks to the much larger capacitance of the design. The maximum reliable clock rate is around 50 kHz. The primary limit to the clock speed is the gate capacitance of the MOSFETs that we are using, which is much larger than the capacitance of the MOSFETs on an original 6502 die.
So if you built a SID using the same techniques and components, you couldn't run it in real-time without the pitch being way too low or without modifying the design. I'm not sure how hard this would be to avoid with better-spec'd components, but intuitively it makes sense for a much larger circuit to run much slower.