This reminds me of coding with AI where slightly changing the prompt gives you different code and you don't really understand why but you use it anyway. In this case it's not even the compiler that's adding the incorrect instructions - it's the linker. Someday we'll just trust the AI to spit out working code the same way we assume that the C toolchain produces reasonable assembler. And when it doesn't it's remarkable enough to warrant a blog post and HN discussion.
At least in the UK the Kia Niro comes in petrol, plug-in hybrid, and full EV versions of the same chassis. It seems like most Uber drivers in London have replaced their hybrid Prius with an EV Niro.
They've updated the ReCaptcha website, but it used to say: "Every time our CAPTCHAs are solved, that human effort helps digitize text, annotate images, and build machine learning datasets."
Google at least uses captchas to gather training data for computer vision ML models. That's why they show pictures of stop lights and buses and motorcycles - so they can train self-driving cars.
Handling SIGINT (ctrl-c) with child processes is tricky, but a more pervasive problem for Rust CLI programs is handling SIGPIPE. For historical reasons the compiler adds a signal handler before calling main() which ignores SIGPIPE. It means when you pipe your Rust CLI program's output to something like head, instead of being killed by the signal sent when head closes the pipe, you get a write error and usually print an error message instead of silently dying. You can match on the type of write error and skip printing a message when it's from a broken pipe, but a more subtle problem is that the shell sets the exit status of a program killed by a signal to 128 + the signal number, so 141 in the case of a broken pipe. You can emulate this behaviour by checking for a broken pipe and explicitly exiting with a 141 status code, but it's not possible to fully reproduce being killed by a signal. There's been an issue to make this configurable (the latest proposal is via a compiler flag) for years.
I expect that Americans spend more time in their basements than people in most other countries (other than Canada). The kind of furnished basement that teenagers hang out in from That 70's Show isn't common elsewhere.
Sure, at some point you have to let the abstraction leak. At the time Sun designed NFS, you could basically count on the fact that the server was some Solaris machine capable of multiple years of uptime on your LAN. Filesystems never made the transition to running over the internet because that was too unreliable and POSIX didn't really provide the right interfaces to expose that.
We're coming to the end of the road with that generation of OS design - MacOS is still Unix and thinks that it's running on a VAX. There's a reason why Macbooks don't come with a 5G modem: because programs would have to be aware of the underlying network. That's why it's inevitable that we'll move to something like IOS (or Android), because every program that uses the network has to handle not only failures but situations like being in flight mode or running on low-bandwidth mobile networks.
The designers of NFS chose to make a distributed system emulate a highly consistent and available system (a hard drive), which was (and is) a reasonable tradeoff. It didn't require every existing tool, such as ls, to deal with things like the server rebooting while listing a directory. (The original NFS protocol is stateless, so clients can survive server reboots.) What does vi do when the server hosting the file you're editing stop responding? None of these tools have that kind of error handling.
I don't know how io_uring solves this - does it return an error if the underlying NFS call times out? How long do you wait for a response before giving up and returning an error?
There's a long history of "directory editor" programs going back to (at least) 1974, which predates emacs. It's not surprising that there are many examples of convergent evolution.
Two relevant quotes from writers who could not be more different:
“I would rather be governed by the first 2,000 people in the Boston telephone directory than by the 2,000 faculty members of Harvard University.” - William F Buckley Jr
“Anyone who is capable of getting themselves made President should on no account be allowed to do the job.” - Douglas Adams
Thanks that actually looks really good! Part of the challenge with some of the crates is that they're great for advanced use but there's so much magic going on with derive macros, which a new Rust programmer won't be familiar with. Most simple CLI programs don't even declare structs so you would have to take a detour to explain that first.
Yes 4 has been out for a while but I was caught in the time when it went from v2 (2020) to v3 (2021) to v4 (2022). I made the mistake of rewriting all the chapters from using 2 to 3, which was quickly deprecated for 4.
I chose clap as the argument parsing crate for my book on Rust CLI programming and it's been my biggest regret. Folk sometimes complain that Rust itself is a moving target but I haven't found that to be the case at all. On the other hand, clap has gone through several, incompatible major releases over the past few years. Unfortunately, in order to keep this forward momentum, it also starts deprecating things which means that I had to pin it to a quickly outdated version. If I ever go back and update the earlier chapters I'd pick a simpler, slower-moving library.
On the other hand, this situation is a great example of why keeping things like argument parsing out of the Rust standard library is such a good idea. It's much better to let the community gel around some crates which are able to iterate (and innovate) faster than something with strict backwards-compatibility requirements. Looking at the discussion here there's clearly not one way to solve this - there's no way that something as large and complex as clap has evolved into will ever become "standard".
On terminals, the control key masked off the 6th and 7th bits of the 8-bit value sent by the letter key. That's a bitwise AND with octal 037, or in decimal subtracting 96 from the lower case key value. This happens in the terminal (or keyboard) itself, so the computer doesn't see it as two pressed keys, it just gets the masked value.
In ASCII 'c' is 99 (decimal), so when modified with control is 3, ETX (end of text), which teletypes used to indicate the end of text. So unix shells reused that control character to terminate the current program with SIGINT. But every letter key maps to a control character at the start of the ASCII range, like 'g' maps to BEL for the bell, or 'h' maps to BS (backspace). '[' maps to ESC (escape) which is handy when Apple decides to move the escape key on their laptop keyboard.
You can disagree with his conclusions but I don't think his understanding of small models is up for debate. This is the person who created micrograd/makemore/nanoGPT and who has produced a ton of educational materials showing how to build small and local models.
Why the hate? It's a genuine question. When you rewrite something, you need to justify the effort somehow. The GNU coreutils started out as "the BSD utilities, but with the GPL!".
Bash isn't the Bourne shell (sh)! It's a replacement (Bourne Again Shell). But it's interesting that the replacement has become so entrenched that folk assume that it was the original.
The GNU utils were a replacement for the BSD utils which were a replacement for the original AT&T utils. Every replacement added new functionality and improvements, and every time someone complained that they didn't stick closer to the thing they replaced. Looking specifically at grep, there used to be new versions like egrep and fgrep that added functionalities beyond standard grep's, but those were eventually pulled into "standard" grep (GNU or BSD). If we stuck with standards we'd all still be using the Bourne shell. The GNU utilities have been around long enough that they feel like the standard now, but I'm glad that we're coming into a new phase of innovation in command-line utilities. And this didn't start with Rust - the new generation of search utilities started with ack (Perl) and then ag (C).
I have a strong memory from the start of my career, when I had a job setting up Solaris systems and there was a whispered rumour that one of the senior admins could read core files. To the rest of us, they were just junk that the system created when a process crashed and that we had to find and delete to save disk space. In my mind I thought she could somehow open the files in an editor and "read" them, like something out of the Matrix. We had no idea that you could load them into a debugger which could parse them into something understandable.
One approach that I don't see often enough is to focus on learning a subset of the language first. For example, in my own book on Rust, I skip teaching lifetimes. It's not necessary to write functions with lifetimes to build quite a few fully functioning programs. The same with macros (although the fact that their signatures are opaque doesn't make it easy for beginners). On the other hand, I disagree with the advice to rely on copy() or clone() - it's better to learn about borrowing from the beginning since it's such a fundamental part of the language.
For some applications it feels like half the signal of whether you're qualified is whether the CV is set in Computer Modern, ie was produced via LaTeX.
Yes that's it: "cat > /dev/null". Or you can even skip running cat and just "> /dev/null". You exit by typing ctrl-d which sends an EOF (and then sends all the output to /dev/null!).