<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Memory-Safety on PHP Boy Scout</title><link>https://phpboyscout.uk/tags/memory-safety/</link><description>Recent content in Memory-Safety on PHP Boy Scout</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><copyright>Matt Cockayne</copyright><lastBuildDate>Sun, 14 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://phpboyscout.uk/tags/memory-safety/index.xml" rel="self" type="application/rss+xml"/><item><title>Everyone wants Rust's safety, nobody wants Rust</title><link>https://phpboyscout.uk/everyone-wants-rusts-safety-nobody-wants-rust/</link><pubDate>Sun, 14 Jun 2026 00:00:00 +0000</pubDate><guid>https://phpboyscout.uk/everyone-wants-rusts-safety-nobody-wants-rust/</guid><description>&lt;img src="https://phpboyscout.uk/everyone-wants-rusts-safety-nobody-wants-rust/cover-everyone-wants-rusts-safety-nobody-wants-rust.png" alt="Featured image of post Everyone wants Rust's safety, nobody wants Rust" /&gt;&lt;p&gt;This spring, the better part of a million lines of Zig quietly became a million
lines of Rust. Bun, the JavaScript runtime that was the showcase for &amp;ldquo;you don&amp;rsquo;t
need a borrow checker, you need good tools and a steady hand&amp;rdquo;, looked at its own
memory bugs and switched teams. Around
&lt;a class="link" href="https://www.techzine.eu/news/devops/141364/bun-takes-a-surprising-step-from-zig-to-rust/" target="_blank" rel="noopener"
 &gt;99.8% of its test suite passed&lt;/a&gt;
on the rewritten code, a clutch of memory leaks closed in the move, and the
maintainers said the quiet part out loud: the previous release would be the last
one written in Zig.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s tempting to read that as Rust winning, hoist the flag, and move on. I don&amp;rsquo;t
think that&amp;rsquo;s quite the story, and the more interesting one is happening everywhere
else at the same time.&lt;/p&gt;
&lt;p&gt;Because Bun is the exception that went all the way. Everyone else is trying to get
the &lt;em&gt;safety&lt;/em&gt; without the &lt;em&gt;Rust&lt;/em&gt;, and watching how they&amp;rsquo;re going about it tells you
more than one runtime&amp;rsquo;s heroic rewrite does.&lt;/p&gt;
&lt;h2 id="what-everyones-actually-after"&gt;What everyone&amp;rsquo;s actually after
&lt;/h2&gt;&lt;p&gt;A quick level-set, because not everyone reading this writes systems code daily.
&amp;ldquo;Memory safety&amp;rdquo; is the property that a program can&amp;rsquo;t read or write memory it has no
business touching: no using a value after you&amp;rsquo;ve freed it, no running off the end
of an array. It sounds niche. It is, by most counts, behind something like
&lt;a class="link" href="https://www.kusari.dev/blog/rust-wont-fix-everything-moving-toward-a-memory-safe-future" target="_blank" rel="noopener"
 &gt;70% of serious security vulnerabilities&lt;/a&gt;,
which is why governments and trillion-dollar companies suddenly care a great deal.&lt;/p&gt;
&lt;p&gt;There are roughly three ways to get it. Rust uses a &lt;em&gt;borrow checker&lt;/em&gt;: a compiler
that flatly refuses to build your program unless it can prove, before it ever runs,
that you never touch memory after you&amp;rsquo;re done with it. The price is that it argues
with you the entire time you&amp;rsquo;re writing. The product is that an entire category of
bug becomes literally unwriteable. Go (and most managed languages) uses a &lt;em&gt;garbage
collector&lt;/em&gt;: a runtime janitor that frees memory for you, so you mostly can&amp;rsquo;t get it
wrong, at the cost of some overhead and a little control. And then there&amp;rsquo;s the old
way, the one most code on Earth still uses: trust the developer to get it right,
and add an &lt;em&gt;escape hatch&lt;/em&gt;, usually a keyword like &lt;code&gt;unsafe&lt;/code&gt;, for the bits where they
promise they have.&lt;/p&gt;
&lt;p&gt;The retrofit trend is everyone in that third camp trying to inch toward the first
two without rewriting the world.&lt;/p&gt;
&lt;h2 id="rust-didnt-invent-any-of-this"&gt;Rust didn&amp;rsquo;t invent any of this
&lt;/h2&gt;&lt;p&gt;Worth saying plainly, because the fan club rarely does: Rust invented almost none
of it. The borrow checker is, &lt;a class="link" href="https://doc.rust-lang.org/reference/influences.html" target="_blank" rel="noopener"
 &gt;by Rust&amp;rsquo;s own admission&lt;/a&gt;,
Cyclone&amp;rsquo;s region-based memory management, from a safe-C experiment in the early
2000s, welded to &lt;a class="link" href="https://borretti.me/article/type-systems-memory-safety" target="_blank" rel="noopener"
 &gt;affine types out of linear logic&lt;/a&gt;,
ideas that predate Rust by decades. And it goes beyond the borrow checker.
Rust&amp;rsquo;s exhaustive pattern matching came from ML and Haskell. Its &amp;ldquo;errors are
values, and there is no null&amp;rdquo; approach, &lt;code&gt;Result&lt;/code&gt; and &lt;code&gt;Option&lt;/code&gt;, is Haskell&amp;rsquo;s Maybe
and Either in work boots.&lt;/p&gt;
&lt;p&gt;What Rust did, and did better than anyone before it, was taste and integration: it
curated thirty-odd years of academic research into one coherent language and proved
the ideas could carry real systems code rather than just research papers. That is
the genuine USP, and it&amp;rsquo;s why the rest of the industry is now shopping from the
same shelf. Pattern matching has landed in Python and Java, with a proposal in
flight for C++26. Swift 6 shipped
&lt;a class="link" href="https://www.infoworld.com/article/3529619/swift-6-arrives-with-improved-concurrency-data-race-safety.html" target="_blank" rel="noopener"
 &gt;compile-time data-race safety&lt;/a&gt;,
its &lt;code&gt;Sendable&lt;/code&gt; machinery a close cousin of Rust&amp;rsquo;s &lt;code&gt;Send&lt;/code&gt; and &lt;code&gt;Sync&lt;/code&gt;. The borrow
checker just gets the headlines because it&amp;rsquo;s the hardest bit to copy. Which makes
the title almost too literal: everyone wants Rust&amp;rsquo;s safety, and they are quietly
adopting its mechanisms one feature at a time.&lt;/p&gt;
&lt;h2 id="credit-where-its-due-c-is-doing-this-properly"&gt;Credit where it&amp;rsquo;s due: C# is doing this properly
&lt;/h2&gt;&lt;p&gt;The example that made me sit up is C#. In C# 16, Microsoft is
&lt;a class="link" href="https://devblogs.microsoft.com/dotnet/improving-csharp-memory-safety/" target="_blank" rel="noopener"
 &gt;redefining the &lt;code&gt;unsafe&lt;/code&gt; keyword&lt;/a&gt;
that&amp;rsquo;s been in the language since version one. Instead of &lt;code&gt;unsafe&lt;/code&gt; marking a lump
of syntax, it now marks a &lt;em&gt;contract&lt;/em&gt;: a promise the compiler can&amp;rsquo;t verify and a
human has to read and uphold, with documentation and static analysers nudging you
to take it seriously. They&amp;rsquo;re even floating badges on NuGet packages to show which
ones have opted in.&lt;/p&gt;
&lt;p&gt;My first instinct with any retrofit is suspicion, because bolting safety onto a
language after the fact has a long and miserable history, and an escape hatch that&amp;rsquo;s
easy to reach is an escape hatch people will reach for the moment they&amp;rsquo;re in a
hurry. But this isn&amp;rsquo;t a bolt-on. Taking the keyword that&amp;rsquo;s already there and giving
it real teeth is working &lt;em&gt;with&lt;/em&gt; the grain of the language instead of stapling a
second safety system alongside the first. That&amp;rsquo;s honest engineering, and it deserves
the credit. It genuinely raises the floor.&lt;/p&gt;
&lt;h2 id="a-contract-is-not-a-guarantee"&gt;A contract is not a guarantee
&lt;/h2&gt;&lt;p&gt;Here&amp;rsquo;s where my enthusiasm meets its limit, and it&amp;rsquo;s a distinction I happen to have
&lt;a class="link" href="https://phpboyscout.uk/forbid-means-forbid-until-linkme-needs-a-word/" &gt;a lot of skin in&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;C#&amp;rsquo;s redefined &lt;code&gt;unsafe&lt;/code&gt; makes dangerous code &lt;em&gt;visible and reviewable&lt;/em&gt;. That is a
real improvement, and most teams would be better off for it. But visible and
reviewable still means a human has to honour the promise. It&amp;rsquo;s a sign on the door.
Rust&amp;rsquo;s equivalent is a wall: in &lt;a class="link" href="https://phpboyscout.uk/rust-tool-base-the-same-idea/" &gt;rust-tool-base&lt;/a&gt;
I put &lt;code&gt;#![forbid(unsafe_code)]&lt;/code&gt; at the top of all eleven shipping crates, and
&lt;code&gt;forbid&lt;/code&gt; is not advice, it&amp;rsquo;s a refusal. The compiler will not build a crate that
contains &lt;code&gt;unsafe&lt;/code&gt;, full stop, and unlike its softer sibling &lt;code&gt;deny&lt;/code&gt;, you can&amp;rsquo;t quietly
switch it back off in a corner of the code where it&amp;rsquo;s inconvenient. The whole reason
I use &lt;code&gt;forbid&lt;/code&gt; and not &lt;code&gt;deny&lt;/code&gt; is that I don&amp;rsquo;t trust future-me, in a hurry, not to
reach for the hatch.&lt;/p&gt;
&lt;p&gt;So when I look at the C# work I think: good, genuinely good, and they should take it
further. A contract a human upholds is not the same kind of thing as a proof a
compiler enforces, and the trend, if it&amp;rsquo;s serious, points at enforcement. Visible is
better than invisible. Impossible is better than visible.&lt;/p&gt;
&lt;h2 id="discipline-never-scaled-and-thats-not-an-insult"&gt;Discipline never scaled, and that&amp;rsquo;s not an insult
&lt;/h2&gt;&lt;p&gt;The objection I keep hearing, and that a younger me would have made, is that any
language can be memory-safe if you&amp;rsquo;re just disciplined enough. And it&amp;rsquo;s true, in the
way that any house can be tidy if you never get busy. In the before times we shipped
memory-safe C with code review and valgrind and sheer bloody-mindedness, and it
worked, sort of, at small scale.&lt;/p&gt;
&lt;p&gt;It doesn&amp;rsquo;t scale, and Bun is the proof sitting on the table. That wasn&amp;rsquo;t a sloppy
team learning the basics. It was a strong team, betting publicly on the
discipline-and-good-tools model, and the memory bugs piled up anyway until the
honest move was to let a compiler take the job. Discipline failing at scale isn&amp;rsquo;t a
moral failure of the engineers. It&amp;rsquo;s just what happens when you ask humans to hold a
thousand invariants in their heads across a million lines. Delegating that to a
machine that never gets tired or rushed isn&amp;rsquo;t laziness. It&amp;rsquo;s the entire point of
having compilers at all.&lt;/p&gt;
&lt;h2 id="the-part-that-changed-my-mind"&gt;The part that changed my mind
&lt;/h2&gt;&lt;p&gt;I learned most of my Rust by building rust-tool-base with an AI alongside me,
leaning on it to explain the borrow checker, suggest the idiomatic shape, and check
my work. And somewhere in that I noticed the thing I now can&amp;rsquo;t unsee: &lt;strong&gt;the borrow
checker is exactly as good a guardrail for the AI as it is for me.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A model, like a tired human, will write a confident use-after-free without blinking.
In Rust it simply doesn&amp;rsquo;t compile, so the mistake never reaches me. What that does
is move the whole error surface. The bugs that survive into review aren&amp;rsquo;t memory
bugs or lifetime bugs or data races, the language has eaten those, they&amp;rsquo;re errors of
&lt;em&gt;logic&lt;/em&gt;: the code is safe and wrong. And logic is precisely where I want my
attention, and the AI&amp;rsquo;s, because it&amp;rsquo;s the part a human has to own and the part the
models are getting better at every month. (I split my AI work across a few providers
for their different strengths, so this is not a pitch for anyone&amp;rsquo;s logo. The effect
is the same whoever&amp;rsquo;s doing the typing.)&lt;/p&gt;
&lt;p&gt;Which dissolves the one argument that ever really kept people out of Rust. &amp;ldquo;The
borrow checker is too much friction&amp;rdquo; was always the case for the defence. But Bun&amp;rsquo;s
million-line rewrite was done largely &lt;em&gt;with&lt;/em&gt; an AI, because an AI is very good at
paying a tax that is tedious and mechanical rather than creative. The friction is
getting cheaper to pay at exactly the moment the guarantee is getting more valuable
to have. In an AI-assisted world, a language that &lt;em&gt;proves&lt;/em&gt; safety is worth more, not
less, because it fences in the machine&amp;rsquo;s mistakes as firmly as your own.&lt;/p&gt;
&lt;h2 id="none-of-this-means-rewrite-everything-in-rust"&gt;None of this means rewrite everything in Rust
&lt;/h2&gt;&lt;p&gt;I want to be careful not to land somewhere smug, because most software does not need
what Rust offers and pretending otherwise is how you end up rewriting a CRUD app
nobody asked you to. Garbage collection is not a failure state. Go&amp;rsquo;s collector keeps
getting &lt;a class="link" href="https://go.dev/doc/go1.26" target="_blank" rel="noopener"
 &gt;meaningfully better&lt;/a&gt;, my own go-tool-base is GC&amp;rsquo;d
top to bottom and I have never once wished it weren&amp;rsquo;t, and &amp;ldquo;safe-by-default with a
GC&amp;rdquo; is the right answer for a vast amount of the work most of us do. The borrow
checker is a price, and you should only pay it when the thing you&amp;rsquo;re buying, that
last class of guarantee with no runtime cost, is something your stakes actually
need.&lt;/p&gt;
&lt;h2 id="what-it-comes-down-to"&gt;What it comes down to
&lt;/h2&gt;&lt;p&gt;The question was never &amp;ldquo;is it as safe as Rust&amp;rdquo;. That framing turns everything into a
loss for everyone who isn&amp;rsquo;t Rust, which is silly. The useful question is: &lt;em&gt;what does
your language make the default, and how hard does it make the escape hatch to reach?&lt;/em&gt;
Go makes safety the default and charges you a GC. Rust makes it the default and
charges you the borrow checker. C# is moving its default in the right direction and,
for now, leaves the hatch as a promise rather than a wall.&lt;/p&gt;
&lt;p&gt;Credit the retrofits, they are raising the floor for an enormous amount of code that
was never going to be rewritten. Just don&amp;rsquo;t mistake the floor for the ceiling, or a
contract a human signs for a guarantee a compiler keeps. Everyone wants Rust&amp;rsquo;s
safety, and the interesting question, now that an AI will pay the toll for you, is
who still has a reason not to want it.&lt;/p&gt;
&lt;p&gt;Widen the lens past Rust, though, because that&amp;rsquo;s where the news gets genuinely good.
We&amp;rsquo;re at a turn in how languages evolve. Compile-time rigour is spreading rather
than retreating: borrow checking is reaching the Python family through Mojo, static
typing long since conquered JavaScript, and even the managed languages are turning
their escape hatches into something you have to argue with. More of our safety is
quietly moving from &amp;ldquo;remember to&amp;rdquo; into &amp;ldquo;can&amp;rsquo;t not&amp;rdquo;. And the one thing that always
made the strict path hard to start down, the friction, is being absorbed by an AI
that will happily learn the rules so you can lean on them. I&amp;rsquo;ve been at this long
enough to distrust a rosy forecast, but I&amp;rsquo;ll put my name to this one: the outlook
for software that&amp;rsquo;s safe and secure by default has never looked better.&lt;/p&gt;</description></item><item><title>A framework that contains no unsafe</title><link>https://phpboyscout.uk/a-framework-that-contains-no-unsafe/</link><pubDate>Tue, 28 Apr 2026 00:00:00 +0000</pubDate><guid>https://phpboyscout.uk/a-framework-that-contains-no-unsafe/</guid><description>&lt;img src="https://phpboyscout.uk/a-framework-that-contains-no-unsafe/cover-a-framework-that-contains-no-unsafe.png" alt="Featured image of post A framework that contains no unsafe" /&gt;&lt;p&gt;&amp;ldquo;It&amp;rsquo;s written in Rust&amp;rdquo; gets thrown around as if it were a memory-safety guarantee. It mostly isn&amp;rsquo;t. Rust is memory-safe by &lt;em&gt;default&lt;/em&gt;, which is a wonderful thing, but the &lt;code&gt;unsafe&lt;/code&gt; keyword exists precisely so any crate, any module, can step outside that default when it needs to. So &amp;ldquo;written in Rust&amp;rdquo; really means &amp;ldquo;mostly safe, probably&amp;rdquo;. rust-tool-base makes the stronger claim about its own code, and gets the compiler to enforce it.&lt;/p&gt;
&lt;h2 id="safe-by-default-is-not-the-same-as-safe"&gt;Safe by default is not the same as safe
&lt;/h2&gt;&lt;p&gt;People reach for Rust because of memory safety, and the reputation is earned. Write ordinary Rust and the compiler will not let you have a use-after-free, a data race, or a buffer overrun. That&amp;rsquo;s the default, and it&amp;rsquo;s a very good default.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s a default, and defaults can be turned off. Rust has an &lt;code&gt;unsafe&lt;/code&gt; keyword precisely so that, when you genuinely need to, you can dereference a raw pointer, call into C, or tell the compiler you&amp;rsquo;ve upheld an invariant it can&amp;rsquo;t check itself. Inside an &lt;code&gt;unsafe&lt;/code&gt; block, the guarantees are yours to maintain, not the compiler&amp;rsquo;s to enforce.&lt;/p&gt;
&lt;p&gt;That keyword has to exist. Some of the most foundational crates in the ecosystem are built on it, carefully. But it means a fact worth being precise about: a project being &amp;ldquo;written in Rust&amp;rdquo; tells you its code is &lt;em&gt;mostly&lt;/em&gt; safe. It does not tell you the project&amp;rsquo;s own code contains &lt;em&gt;no&lt;/em&gt; &lt;code&gt;unsafe&lt;/code&gt;. Those are different claims, and only the second one is a guarantee.&lt;/p&gt;
&lt;p&gt;rust-tool-base makes the second claim about its own code, and has the compiler back it up.&lt;/p&gt;
&lt;h2 id="forbid-not-just-deny"&gt;&lt;code&gt;forbid&lt;/code&gt;, not just &lt;code&gt;deny&lt;/code&gt;
&lt;/h2&gt;&lt;p&gt;The mechanism is one line at the top of every crate:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#![forbid(unsafe_code)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;unsafe_code&lt;/code&gt; is a lint, and Rust lints have levels. The interesting choice is &lt;code&gt;forbid&lt;/code&gt; rather than &lt;code&gt;deny&lt;/code&gt;, because the two are not the same strength.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;deny&lt;/code&gt; makes the lint an error. But it&amp;rsquo;s an error a &lt;em&gt;downstream module can locally override&lt;/em&gt;. Anyone can write &lt;code&gt;#[allow(unsafe_code)]&lt;/code&gt; on a function or a block and the &lt;code&gt;deny&lt;/code&gt; is lifted right there. As a policy, &lt;code&gt;deny&lt;/code&gt; is &amp;ldquo;don&amp;rsquo;t do this unless you really mean to&amp;rdquo;, and &amp;ldquo;unless you really mean to&amp;rdquo; is a door.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;forbid&lt;/code&gt; is the strict one. It makes the lint an error &lt;em&gt;and&lt;/em&gt; it makes that error impossible to override from inside the crate. A module cannot &lt;code&gt;#[allow]&lt;/code&gt; its way back out. Once a crate root says &lt;code&gt;#![forbid(unsafe_code)]&lt;/code&gt;, there&amp;rsquo;s no &lt;code&gt;unsafe&lt;/code&gt; anywhere in that crate, and no local exception can be carved out. The compiler simply refuses.&lt;/p&gt;
&lt;p&gt;So every rust-tool-base crate that ships in a built tool forbids &lt;code&gt;unsafe&lt;/code&gt; at its root. Not &amp;ldquo;discourages&amp;rdquo;. Cannot contain it.&lt;/p&gt;
&lt;h2 id="the-one-subtlety"&gt;The one subtlety
&lt;/h2&gt;&lt;p&gt;There&amp;rsquo;s a wrinkle, and it&amp;rsquo;s worth showing rather than hiding, because it&amp;rsquo;s where the design got specific.&lt;/p&gt;
&lt;p&gt;The workspace sets &lt;a class="link" href="https://gitlab.com/phpboyscout/rust-tool-base/-/blob/9c22aa8/Cargo.toml#L43" target="_blank" rel="noopener"
 &gt;&lt;code&gt;unsafe_code = &amp;quot;deny&amp;quot;&lt;/code&gt;&lt;/a&gt; as the baseline for &lt;em&gt;everything&lt;/em&gt;, including test files. But test code occasionally has a real need for &lt;code&gt;unsafe&lt;/code&gt;. In the 2024 edition, &lt;code&gt;std::env::set_var&lt;/code&gt; became &lt;code&gt;unsafe&lt;/code&gt;, because mutating the process environment isn&amp;rsquo;t thread-safe, and a test that exercises environment-driven configuration has to call it.&lt;/p&gt;
&lt;p&gt;So the split is deliberate. The workspace-wide level is &lt;code&gt;deny&lt;/code&gt;, which a test file can locally &lt;code&gt;#[allow]&lt;/code&gt; when it genuinely needs that one environment call. But every production &lt;code&gt;lib.rs&lt;/code&gt; and &lt;code&gt;main.rs&lt;/code&gt; additionally carries &lt;code&gt;#![forbid(unsafe_code)]&lt;/code&gt;, and &lt;code&gt;forbid&lt;/code&gt; cannot be relaxed. Test scaffolding gets a controlled, visible exception for a specific standard-library call. Shipping code gets none. The guarantee that matters, &amp;ldquo;the code in the binary contains no &lt;code&gt;unsafe&lt;/code&gt;&amp;rdquo;, holds, and the place it&amp;rsquo;s slightly loosened is exactly the place that never reaches a user.&lt;/p&gt;
&lt;h2 id="what-the-guarantee-is-actually-worth"&gt;What the guarantee is actually worth
&lt;/h2&gt;&lt;p&gt;Two things, one for users and one for reviewers.&lt;/p&gt;
&lt;p&gt;For users: an entire family of bug is ruled out of first-party code mechanically. Use-after-free, double-free, data races on shared memory, reading off the end of a buffer. These are the classic memory-safety vulnerabilities, and in a crate that forbids &lt;code&gt;unsafe&lt;/code&gt; they cannot originate, because the constructs that produce them cannot be written. That&amp;rsquo;s not careful coding. It&amp;rsquo;s the compiler refusing to build anything else.&lt;/p&gt;
&lt;p&gt;For reviewers: the cost of an &lt;code&gt;unsafe&lt;/code&gt; block is mostly the review burden it carries. Every one is a spot where a human has to check, by hand, that an invariant holds, and has to re-check it whenever nearby code changes. A crate that forbids &lt;code&gt;unsafe&lt;/code&gt; has zero of those. There&amp;rsquo;s no &lt;code&gt;unsafe&lt;/code&gt; block to audit, ever, because the compiler guarantees there isn&amp;rsquo;t one.&lt;/p&gt;
&lt;p&gt;The promise has a boundary. It covers rust-tool-base&amp;rsquo;s &lt;em&gt;own&lt;/em&gt; code; its dependencies are another matter, and some of them do contain &lt;code&gt;unsafe&lt;/code&gt;, correctly. Keeping that side honest is a different job, done by &lt;a class="link" href="https://phpboyscout.uk/waivers-with-an-expiry-date/" &gt;vetting the dependency tree and gating it in CI&lt;/a&gt;. Within first-party code, though, the guarantee is real, and there&amp;rsquo;s no Go equivalent to it. Go has an &lt;code&gt;unsafe&lt;/code&gt; package, but nothing that lets a codebase prove, to the compiler, that it never touches it.&lt;/p&gt;
&lt;h2 id="the-bottom-line"&gt;The bottom line
&lt;/h2&gt;&lt;p&gt;Rust is memory-safe by default, but the &lt;code&gt;unsafe&lt;/code&gt; keyword exists so that default can be set aside. &amp;ldquo;Written in Rust&amp;rdquo; therefore does not by itself mean a project&amp;rsquo;s own code contains no &lt;code&gt;unsafe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;rust-tool-base makes that the stronger claim. Every crate root carries &lt;code&gt;#![forbid(unsafe_code)]&lt;/code&gt;, and &lt;code&gt;forbid&lt;/code&gt;, unlike &lt;code&gt;deny&lt;/code&gt;, cannot be overridden from inside the crate. Test files get a narrow, visible &lt;code&gt;deny&lt;/code&gt;-level exception for the one standard-library call that needs it; shipping code gets none. The payoff is a whole class of memory-safety bug ruled out of first-party code by construction, and not one &lt;code&gt;unsafe&lt;/code&gt; block left for a reviewer to audit.&lt;/p&gt;</description></item></channel></rss>