Van Graydon Hoare's experiment uit 2006 tot het huidige Rust-ecosysteem: zie hoe geheugenveiligheid zonder garbage collection de systeemprogrammering veranderde.

Dit artikel vertelt een gericht oorsprongsverhaal: hoe Graydon Hoare’s persoonlijke experiment uitgroeide tot Rust, en waarom de ontwerpskeuzes van Rust genoeg impact hadden om de verwachtingen rond systeemprogrammering te verschuiven.
“Systems programming” werkt dicht bij de machine — en dicht bij de risico’s van je product. Het komt terug in browsers, game-engines, onderdelen van besturingssystemen, databases, netwerken en embedded software — plekken waar je doorgaans nodig hebt:
Historisch zorgde die combinatie ervoor dat teams voor C en C++ kozen, aangevuld met uitgebreide regels, reviews en tooling om geheugen-gerelateerde bugs te verminderen.
De kopzin van Rust is makkelijk te zeggen en moeilijk te realiseren:
Geheugenveiligheid zonder garbage collector.
Rust wil veelvoorkomende fouten zoals use-after-free, double-free en veel soorten data races voorkomen — zonder te vertrouwen op een runtime die periodiek de uitvoering pauzeert om geheugen vrij te geven. In plaats daarvan verschuift Rust veel van dat werk naar compile-tijd via ownership en borrowing.
Je krijgt de geschiedenis (van vroege ideeën tot Mozillas betrokkenheid) en de kernconcepten (ownership, borrowing, lifetimes, safe vs. unsafe) in eenvoudige taal uitgelegd.
Wat je niet krijgt is een volledige Rust-tutorial, een complete syntaxis-tour of stapsgewijze projectsetup. Zie dit als het “waarom” achter Rust’s ontwerp, met genoeg voorbeelden om de ideeën concreet te maken.
Writers note: het volledige stuk is gericht op ~3.000 woorden, zodat er ruimte is voor korte voorbeelden zonder een naslagwerk te worden.
Rust begon niet als een commissie-ontworpen “next C++”. Het begon als een persoonlijk experiment van Graydon Hoare in 2006 — werk dat hij aanvankelijk zelfstandig deed voordat het bredere aandacht trok. Die oorsprong telt: veel vroege ontwerpskeuzes lezen als pogingen om dagelijkse pijnpunten op te lossen, niet om taalkunde te winnen.
Hoare onderzocht hoe je laag-niveau, hoog-presterende software kunt schrijven zonder garbage collection — en tegelijk de meest voorkomende oorzaken van crashes en beveiligingsbugs in C en C++ kunt vermijden. De spanning klinkt vertrouwd voor systeemprogrammeurs:
Rust’s koers “geheugenveiligheid zonder GC” was in het begin geen marketingtagline. Het was een ontwerpproject: houd prestatiekenmerken geschikt voor systeemwerk, maar maak veel categorieën geheugenbugs moeilijker uit te drukken.
Het is terecht om te vragen waarom dit geen "betere compiler" voor C/C++ kon zijn. Tools zoals statische analyse, sanitizers en veiligere libraries vangen veel problemen, maar ze kunnen over het algemeen geen garantie geven voor geheugenveiligheid. De onderliggende talen staan patronen toe die moeilijk — of onmogelijk — van buitenaf volledig te controleren zijn.
Rust’s gok was om sleutelregels in de taal en het typesysteem te verplaatsen zodat veiligheid een standaarduitkomst wordt, terwijl handmatige controle nog steeds mogelijk is in duidelijk gemarkeerde escape-hatches.
Sommige details over Rust’s vroegste dagen circuleren als anekdotes (vaak herhaald in talks en interviews). Bij het vertellen van dit oorsprongsverhaal helpt het om onderscheid te maken tussen goed gedocumenteerde mijlpalen — zoals de start in 2006 en Rust’s latere adoptie bij Mozilla Research — en persoonlijke herinneringen of secundaire hervertellingen.
Voor primaire bronnen kun je zoeken naar vroege Rust-documentatie en ontwerpnotities, Graydon Hoare talks/interviews, en posts uit de Mozilla/Servo-periode die beschrijven waarom het project werd opgepakt en hoe de doelen werden geformuleerd. Een degelijke “verdere lectuur” sectie kan lezers naar die originelen verwijzen (zie /blog voor gerelateerde items).
Systems programming betekent vaak werken dichtbij de hardware. Die nabijheid maakt code snel en resource-efficiënt. Het is ook waarom geheugenfouten zo zwaar wegen.
Een paar klassieke fouten komen steeds terug:
Deze fouten zijn niet altijd duidelijk. Een programma kan weken “werken” en dan alleen onder zeldzame timing- of inputpatronen crashen.
Testen bewijst dat iets werkt voor de gevallen die je hebt geprobeerd. Geheugenbugs verbergen zich vaak in de gevallen die je niet probeerde: ongewone inputs, andere hardware, kleine timingverschillen, of een nieuwe compiler-versie. Ze kunnen ook non-deterministisch zijn — vooral in multithreaded programma’s — waardoor de bug verdwijnt zodra je logging toevoegt of een debugger koppelt.
Als geheugen misgaat, krijg je geen nette foutmelding. Je krijgt corrupte state, onvoorspelbare crashes en beveiligingslekken waar aanvallers actief naar zoeken. Teams besteden enorme moeite aan het achtervolgen van fouten die moeilijk te reproduceren en nog moeilijker te diagnosticeren zijn.
Laag-niveau software kan niet altijd “betalen” voor veiligheid met zware runtime-controles of constante geheugeninspectie. Het doel lijkt meer op het lenen van een gereedschap uit een gedeelde werkplaats: je mag het gebruiken, maar de regels moeten helder zijn — wie het vasthoudt, wie het deelt en wanneer het terug moet. Traditionele systeemtalen lieten die regels aan menselijke discipline over. Rust’s oorsprong begint met het bevragen van die ruil.
Garbage collection is een veelgebruikte manier om geheugenfouten te voorkomen. In plaats van dat je zelf geheugen vrijgeeft, houdt de runtime bij welke objecten nog bereikbaar zijn en ruimt de rest op. Dat elimineert hele categorieën problemen — use-after-free, double frees en veel leaks — omdat het programma niet op dezelfde manier “vergeet” op te ruimen.
GC is niet “slecht”, maar het verandert het prestatieprofiel van een programma. De meeste collectors introduceren een combinatie van:
Voor veel toepassingen — web-backends, business-software, tooling — zijn die kosten acceptabel of zelfs onzichtbaar. Moderne GCs zijn uitstekend en verhogen de productiviteit enorm.
In systeemprogrammering telt het slechtste geval vaak het meest. Een browserengine heeft vloeiende rendering nodig; een embedded controller kan strikte timing-eisen hebben; een low-latency server streeft naar strakke tail-latency onder load. In zulke omgevingen is “meestal snel” minder waardevol dan “altijd voorspelbaar”.
Rust’s grote belofte was: behoud C/C++-achtige controle over geheugen en datalayout, maar lever geheugenveiligheid zonder te vertrouwen op een garbage collector. Het doel is voorspelbare prestatiekenmerken — terwijl veilige code de default blijft.
Dit is geen claim dat GC inferieur is. Het is een weddenschap dat er een belangrijke middenweg is: software die laag-niveau controle en moderne veiligheidsgaranties nodig heeft.
Ownership is Rust’s eenvoudigste grote idee: elke waarde heeft één eigenaar die verantwoordelijk is voor het opruimen wanneer de waarde niet meer nodig is.
Die ene regel vervangt veel handmatig “wie freed dit?”-boekhouden dat C- en C++-programmeurs vaak in hun hoofd bijhouden. In plaats van op discipline te vertrouwen, maakt Rust opruimen voorspelbaar.
Als je iets kopieert, heb je twee onafhankelijke versies. Als je iets verplaatst (move), geef je het origineel af — na de move mag de oude variabele het niet meer gebruiken.
Rust behandelt veel heap-gealloceerde waarden (zoals strings, buffers of vectors) standaard als moved. Ze blind kopiëren kan duur en verwarrend zijn: als twee variabelen denken eigenaar van dezelfde allocatie te zijn, ontstaat een risico op geheugenfouten.
Hier het idee in mini-pseudo-code (ongewijzigd in een codeblok):
buffer = make_buffer()
ownerA = buffer // ownerA owns it
ownerB = ownerA // move ownership to ownerB
use(ownerA) // not allowed: ownerA no longer owns anything
use(ownerB) // ok
// when ownerB ends, buffer is cleaned up automatically
Omdat er altijd precies één eigenaar is, weet Rust precies wanneer een waarde opgeruimd moet worden: wanneer de eigenaar uit scope gaat. Dat betekent automatisch geheugenbeheer (je hoeft niet overal free() te roepen) zonder een garbage collector die periodiek door het programma moet lopen.
Deze ownership-regel blokkeert een grote klasse klassieke problemen:
Rust’s ownership-model moedigt niet alleen veiliger gedrag aan — het maakt veel onveilige toestanden onuitdrukbaar, en vormt zo de basis waarop de overige veiligheidsvoorzieningen van Rust bouwen.
Ownership legt uit wie een waarde “bezit”. Borrowing legt uit hoe andere delen van het programma die waarde tijdelijk kunnen gebruiken zonder die over te nemen.
Als je iets in Rust leent, krijg je een referentie. De oorspronkelijke eigenaar blijft verantwoordelijk voor het vrijgeven van het geheugen; de lener mag het alleen tijdelijk gebruiken.
Rust kent twee soorten borrows:
&T): alleen-lezen toegang.&mut T): lees-schrijftoegang.Rust’s centrale borrowingregel is eenvoudig te zeggen en krachtig in de praktijk:
Die regel voorkomt een veelvoorkomende klasse bugs: een deel van het programma leest data terwijl een ander deel het eronder verandert.
Een referentie is alleen veilig als hij nooit langer bestaat dan het ding waar hij naar wijst. Rust noemt die duur een lifetime — de periode waarin de referentie gegarandeerd geldig is.
Je hebt geen formele wiskunde nodig om dit te gebruiken: een referentie mag niet blijven bestaan nadat de eigenaar verdwenen is.
Rust handhaaft deze regels tijdens compilatie via de borrow checker. In plaats van te hopen dat tests een slechte referentie of een risicovolle mutatie vangen, weigert Rust code te bouwen die geheugen onjuist zou kunnen gebruiken.
Denk aan een gedeeld document:
Concurrency is waar “het werkt op mijn machine”-bugs zich verstoppen. Wanneer twee threads tegelijk draaien, kunnen ze op verrassende manieren interacteren — vooral als ze data delen.
Een data race gebeurt wanneer:
Het resultaat is niet alleen “verkeerde output”. Data races kunnen state corrumperen, programma’s laten crashen of beveiligingslekken introduceren. Ze zijn vaak intermittent en verdwijnen soms als je logging toevoegt.
Rust neemt een ongewone houding aan: in plaats van elke programmeur steeds opnieuw te vertrouwen, probeert het veel onveilige concurrency-patronen onuitdrukbaar te maken in veilige code.
Op hoog niveau reiken ownership- en borrowingregels verder dan enkel single-threaded code. Ze bepalen ook wat je mag delen tussen threads. Als de compiler niet kan bewijzen dat gedeelde toegang gecoördineerd is, zal hij de code niet compileren.
Hiermee bedoelen mensen wat ze zeggen met “veilige concurrency” in Rust: je schrijft nog steeds concurrerende programma’s, maar een hele categorie van "oeps, twee threads schreven hetzelfde" fouten wordt vóór uitvoering opgevangen.
Stel je twee threads voor die dezelfde teller ophogen:
Rust verbiedt laag-niveau concurrency-trucs niet. Het quarantaineert ze. Als je echt iets moet doen dat de compiler niet kan verifiëren, kun je unsafe gebruiken — dat werkt als een waarschuwingslabel: “hier is menselijke verantwoordelijkheid vereist.” Die scheiding houdt het grootste deel van je codebasis in de veilige subset, terwijl systemen-power nog steeds mogelijk is waar gerechtvaardigd.
Rust’s reputatie voor veiligheid kan absoluut klinken, maar nauwkeuriger is: Rust maakt de grens tussen veilig en onveilig programmeren expliciet — en makkelijker te auditen.
Het merendeel van Rust-code is “safe Rust.” Hier dwingt de compiler regels af die veelvoorkomende geheugenfouten voorkomen: use-after-free, double free, dangling pointers en data races. Je kunt nog steeds logische fouten maken, maar je kunt niet per ongeluk geheugenveiligheid schenden met normale taalfeatures.
Een belangrijk punt: safe Rust is niet “tragere Rust.” Veel high-performance programma’s zijn volledig in safe Rust geschreven omdat de compiler agressief kan optimaliseren zodra hij erop kan vertrouwen dat regels worden nageleefd.
unsafe bestaat omdat systeemprogrammering soms mogelijkheden nodig heeft die de compiler niet in het algemeen als veilig kan bewijzen. Typische redenen zijn:
Het gebruik van unsafe schakelt niet alle checks uit. Het staat alleen een kleine set operaties toe (zoals het dereferencen van raw pointers) die anders verboden zijn.
Rust dwingt je unsafe blocks en unsafe functies te markeren, waardoor risico zichtbaar wordt in code review. Een veelvoorkomend patroon is een kleine “unsafe core” te houden die ingepakt is in een veilige API, zodat het grootste deel van het programma in safe Rust blijft terwijl een klein, goed gedefinieerd stuk de noodzakelijke invarianten onderhoudt.
Behandel unsafe als een werktuig:
unsafe blocks klein en gelokaliseerd.unsafe code.Goed toegepast wordt unsafe een gecontroleerde interface naar de onderdelen van systeemprogrammering die nog handmatige precisie nodig hebben — zonder dat je de veiligheidsvoordelen van Rust elders opgeeft.
Rust werd niet “echt” omdat het slimme ideeën op papier had — het werd echt omdat Mozilla die ideeën onder druk zette.
Mozilla Research zocht manieren om performance-kritische browsercomponenten te bouwen met minder beveiligingsbugs. Browserengines zijn berucht complex: ze parsen onbetrouwbare input, beheren enorme hoeveelheden geheugen en draaien sterk concurrerende workloads. Die combinatie maakt geheugenveiligheidsfouten en race-conditions zowel veelvoorkomend als duur.
Ondersteuning voor Rust paste bij dat doel: behoud de snelheid van systeemprogrammering en reduceer hele categorieën kwetsbaarheden. Mozilla’s betrokkenheid gaf ook een signaal: Rust was niet alleen Graydon Hoare’s persoonlijke experiment, maar een taal die tegen een van de zwaarste codebases op aarde getest kon worden.
Servo — het experimentele browserengine-project — werd een zichtbaar terrein om Rust op schaal te beproeven. Het doel was niet de browsermarkt te winnen. Servo was een lab waar taalfeatures, compilerdiagnostiek en tooling getest konden worden onder echte beperkingen: buildtijden, cross-platform ondersteuning, developer experience, performance-tuning en correctheid onder parallelisme.
Belangrijker nog hielp Servo het ecosysteem rond de taal te vormen: libraries, build tooling, conventies en debuggingpraktijken die ertoe doen zodra je voorbij toy-programma’s komt.
Echte projecten creëren feedbackloops die taalontwerp niet kan faken. Wanneer ingenieurs frictie ervaren — onduidelijke foutmeldingen, ontbrekende bibliotheken, onhandige patronen — komen die pijnpunten snel naar boven. In de loop van de tijd hielp die constante druk Rust te rijpen van veelbelovend concept naar iets dat teams konden vertrouwen voor grote, prestatiekritische software.
Als je Rust’s bredere evolutie na deze fase wilt verkennen, zie /blog/rust-memory-safety-without-gc.
Rust zit in een middenveld: het streeft naar de prestaties en controle die men van C en C++ verwacht, maar probeert een grote klasse bugs te verwijderen die die talen vaak overlaten aan discipline, testen en geluk.
In C en C++ beheren ontwikkelaars geheugen direct — alloceren, vrijgeven en zorgen dat pointers geldig blijven. Die vrijheid is krachtig, maar maakt het ook makkelijk use-after-free, double-free, buffer overflows en subtiele lifetime-bugs te introduceren. De compiler vertrouwt je meestal.
Rust keert die relatie om. Je behoudt laag-niveau controle (stack vs heap beslissingen, voorspelbare layouts, expliciete eigendomsoverdrachten), maar de compiler dwingt regels af over wie een waarde bezit en hoe lang referenties mogen bestaan. In plaats van “wees voorzichtig met pointers” zegt Rust: “bewijs veiligheid aan de compiler”, en hij compileert geen code die die garanties in safe Rust kan schenden.
Garbage-collected talen (zoals Java, Go of C#) ruilen handmatig geheugenbeheer in voor gemak: objecten worden automatisch vrijgegeven wanneer ze niet meer bereikbaar zijn. Dat kan de productiviteit sterk verhogen.
Rust’s belofte — “geheugenveiligheid zonder GC” — betekent dat je niet betaalt voor een runtime garbage collector, wat helpt wanneer je strakke controle over latency, geheugenvoetafdrukken, opstarttijd of gebruik in beperkte omgevingen nodig hebt. De afweging is dat je ownership expliciet modelleert en de compiler het afdwingt.
Rust kan in het begin zwaarder aanvoelen omdat hij een nieuw denkmodel leert: je denkt in termen van ownership, borrowing en lifetimes, niet alleen “geef een pointer door en hoop dat het goed gaat”. Vroege wrijving verschijnt vaak bij het modelleren van gedeelde state of complexe objectgrafen.
Rust blinkt uit bij teams die security-gevoelige en prestatiekritische software bouwen — browsers, networking, cryptografie, embedded en backend-services met strikte betrouwbaarheidsbehoeften. Als je team snelle iteratie boven laag-niveau controle waardeert, is een GC-taal vaak beter.
Rust is geen universele vervanging; het is een sterke optie als je C/C++-klasse prestaties wil met veiligheidsgaranties waarop je kunt vertrouwen.
Rust won geen aandacht door simpelweg “een vriendelijkere C++” te zijn. Het veranderde het gesprek door te stellen dat laag-niveau code tegelijk snel, geheugenveilig en expliciet over kosten kan zijn.
Vóór Rust zagen teams geheugenfouten vaak als een belasting die je voor prestaties betaalde, en vertrouwden op testen, code review en post-incident fixes om het risico te beheersen. Rust deed een andere gok: encodeer gemeenschappelijke regels (wie data bezit, wie het mag muteren, wanneer het geldig moet blijven) in de taal zodat hele categorieën bugs tijdens compilatie worden afgewezen.
Die verschuiving was belangrijk omdat het niet vroeg dat ontwikkelaars "perfect" moesten zijn. Het vroeg hen duidelijk te zijn — en liet de compiler die duidelijkheid afdwingen.
Rust’s invloed zie je in een mix van signalen in plaats van één krantenkop: groeiende interesse van bedrijven die prestatiegevoelige software uitrollen, meer aanwezigheid in universitaire cursussen, en tooling die minder “onderzoek” en meer “daily driver” aanvoelt (package management, formattering, linting en documentatiestromen die uit de doos werken).
Dat wil niet zeggen dat Rust altijd de beste keuze is — maar het betekent wel dat safety-by-default nu een realistische verwachting is, geen luxe.
Rust wordt vaak geëvalueerd voor:
“Nieuwe standaard” betekent niet dat elk systeem in Rust wordt herschreven. Het betekent dat de lat is verlegd: teams vragen zich vaker af: Waarom accepteren we geheugen-onveilige standaardinstellingen als dat niet nodig is? Zelfs wanneer Rust niet wordt aangenomen, heeft het model de omgeving geduwd richting veiligere APIs, helderdere invarianten en betere tooling voor correctheid.
Als je meer engineering-backstories wilt lezen, bekijk /blog voor gerelateerde posts.
Het oorsprongsverhaal van Rust heeft een eenvoudige rode draad: het bijproject van één persoon (Graydon Hoare die experimenteert met een nieuwe taal) botste op een hardnekkig systeemprogrammeerprobleem, en de oplossing bleek zowel strikt als praktisch.
Rust herschikte een ruil die veel ontwikkelaars als onvermijdelijk beschouwden:
De praktische verschuiving is niet alleen “Rust is veiliger.” Het is dat veiligheid een standaard eigenschap van de taal kan zijn, in plaats van een best-effort discipline die door code reviews en testen wordt afgedwongen.
Als je nieuwsgierig bent, hoef je geen grote herstructurering te doen om te voelen hoe Rust werkt.
Begin klein:
Als je een zachte instap wilt, kies één “dun snee” doel — bijvoorbeeld “een bestand lezen, transformeren en resultaat wegschrijven” — en focus op duidelijke code in plaats van slimme toeren.
Als je een Rust-component prototypeert binnen een groter product, kan het helpen de omliggende onderdelen snel te houden (admin UI, dashboards, control plane, eenvoudige APIs) terwijl je de kernsysteemslogica rigoureus houdt. Platforms zoals Koder.ai kunnen dat soort “lijm”-ontwikkeling versnellen via een chat-gedreven workflow — zodat je een React-front end, een Go-backend en een PostgreSQL-schema snel genereert, de bron exporteert en integreert met je Rust-service over heldere grenzen.
Systems programming is werk dat dicht bij de hardware en bij productdelen met hoge risico's staat — zoals browserengines, databases, OS-componenten, netwerksoftware en embedded software.
Het vraagt meestal om voorspelbare prestaties, laag-niveau geheugen-/controle en hoge betrouwbaarheid, omdat crashes en beveiligingsfouten bijzonder kostbaar zijn.
Het betekent dat Rust probeert veelvoorkomende geheugenfouten (zoals use-after-free en double-free) te voorkomen zonder te vertrouwen op een runtime garbage collector.
In plaats van dat een collector tijdens uitvoering geheugen scant en vrijgeeft, verplaatst Rust veel van die veiligheidscontroles naar compile-tijd met behulp van ownership- en borrowingregels.
Gereedschappen zoals sanitizers en statische analyse vangen veel problemen, maar ze kunnen doorgaans geen volledige garantie geven voor geheugenveiligheid wanneer de taal zelf patronen toelaat die moeilijk of onmogelijk van buitenaf te verifiëren zijn.
Rust verankert cruciale regels in de taal en het typesysteem zodat de compiler hele categorieën bugs standaard kan weigeren, terwijl er nog steeds expliciete uitwegen zijn wanneer dat nodig is.
GC kan runtime-overhead en, nog belangrijker in sommige systemen, minder voorspelbare latency introduceren (bijv. pauzes of verzamelwerk op onhandige momenten).
In domeinen zoals browsers, real-time-achtige controllers of laag-latentie services telt het slechtste geval, daarom richt Rust zich op veiligheid zonder concessies aan voorspelbaarheid.
Ownership betekent dat elke waarde precies één “verantwoordelijke” heeft (de eigenaar). Wanneer die eigenaar uit scope gaat, wordt de waarde automatisch opgeruimd.
Dit maakt opruimen voorspelbaar en voorkomt dat twee plekken denken dat ze hetzelfde geheugen moeten vrijgeven.
Een move geeft eigendom door van de ene variabele naar de andere; de originele variabele mag de waarde daarna niet meer gebruiken.
Dat voorkomt per ongeluk "twee eigenaren van één allocatie", een veelvoorkomende oorzaak van double-free en use-after-free fouten in talen met handmatig geheugenbeheer.
Borrowing laat code tijdelijk een waarde gebruiken via referenties zonder het eigendom over te nemen.
De kernregel is: veel lezers of één schrijver — je kunt meerdere gedeelde referenties (&T) hebben of één muterende referentie (&mut T), maar niet beide tegelijk. Dit voorkomt veel aliasing- en mutatie- tijdens-lezen-fouten.
Een lifetime is de duur waarvoor een referentie geldig is. Rust vereist dat referenties nooit langer leven dan de data waarnaar ze verwijzen.
De borrow checker handhaaft dit tijdens compilatie, zodat code die naar dangling-referenties zou kunnen leiden, wordt geweigerd voordat ze draait.
Een data race ontstaat als meerdere threads tegelijkertijd hetzelfde geheugen benaderen, minstens één toegang schrijft, en er geen coördinatie is.
Rust’s ownership- en borrowingregels gelden ook voor gedeelde toegang tussen threads: onveilige deelformaten zijn moeilijk of onmogelijk om uit te drukken in veilige code, waardoor je wordt gedwongen expliciete synchronisatie of message passing te gebruiken.
De meeste code is geschreven in veilige Rust, waarbij de compiler geheugenveiligheidsregels afdwingt.
unsafe is een expliciete uitweg voor operaties die de compiler in het algemeen niet kan bewijzen als veilig (zoals bepaalde FFI-calls of laag-niveau primitieve bewerkingen). Een gangbare praktijk is om unsafe klein te houden en te verpakken in een veilige API, zodat het eenvoudiger te auditen is tijdens code review.