The Unsafe Rust an AI Wrote Me Last Week A developer reported that an AI-generated lock-free SPSC ring buffer in Rust compiled and passed unit tests but contained a torn read due to incorrect memory ordering. The bug, which would surface under load or on ARM, stemmed from the model's inability to infer concurrency invariants, highlighting risks of using AI for unsafe code. The developer emphasized that human review remains essential for catching such subtle issues. I told a model to write a lock-free SPSC ring buffer in Rust. The result compiled first time, passed my unit tests, and looked utterly reasonable. It also contained a torn read that would only appear at scale. Here is the interesting part, snipped down a bit: php pub fn push &self, value: T - bool { let head = self.head.load Ordering::Relaxed ; let next = head + 1 % self.capacity; if next == self.tail.load Ordering::Relaxed { return false; // full } unsafe { self.buffer.get .as mut ptr .add head .write value ; } self.head.store next, Ordering::Relaxed ; true } Note the orderings: all Relaxed . On the producer side: write data to slot head , then store the updated head. On the consumer side: read the current head, then read data from that slot. The problem is that with Relaxed ordering, nothing guarantees those two producer operations become visible in that order to the consumer. The consumer can observe the updated head pointer before the data write actually becomes visible to it. So the consumer reads head, sees "yes there's data", goes to read it, and instead reads whatever garbage is currently sitting in that memory slot. That is the torn read. Under x86 with its relatively strong memory ordering it might work for a long time. On ARM, or simply under enough load, that ordering will break. The fix is simple: // producer publishes the data with Release self.head.store next, Ordering::Release ; // consumer reads the index with Acquire let head = self.head.load Ordering::Acquire ; The Release store and Acquire load order the data write before the index update the consumer synchronizes on, which closes the race. The point, the actual point, is this. The bug did not exist because the model is stupid. It exists because the model does not understand what this code is for. It looked at many ring buffers in its training data, most of them using Relaxed , and saw Relaxed scattered around in contexts where it happened to be safe for other reasons. It cannot tell the difference between "Relaxed happened to work here because of some higher-level guarantee" and "Relaxed causes a data race in this pattern". That difference lives entirely in the memory model and the actual concurrency of the program. It is not visible in the shape of the code. This is why you should not let these things generate unsafe . Safe Rust is no problem, the compiler says no. But unsafe is exactly where invariants the compiler cannot see and the model has no concept of become critical. You get something that compiles and passes your basic tests, with the bug merely scheduled for production to unearth. I have written enough of these by hand that I tend to catch them in review. That is the real job now. Not writing the code, but knowing what questions the code has to answer, and noticing when the answers aren't there.