- Das Projekt Rust for Linux treibt zentrale Sprachfunktionen für die Kernel-Entwicklung voran und trägt damit zur Weiterentwicklung der Sprache Rust selbst bei
- Drei Punkte stehen im Mittelpunkt: Field Projection, In-place Initialization und Arbitrary Self Types
- Diese Funktionen ermöglichen es, kernelspezifische Strukturen wie Smart Pointer, fest angehefteten Speicher (Pin) und RCU in Rust natürlicher auszudrücken
- Das Rust-Team legt großen Wert auf stabiles Design, weshalb die Entwicklung langsam verläuft, doch dank des klaren Ziels Linux-Kernel steigt die Fokussierung der Entwicklung
- Die Änderungen werden sich auch auf das Rust-Ökosystem außerhalb des Kernels auswirken und voraussichtlich beim Umgang mit Smart Pointern und bei der Vereinfachung von Code stark helfen
Verbesserungen der Rust-Sprache und die Rolle des Linux-Kernels
- Der Grund, warum die Entwicklung neuer Funktionen in Rust langsam ist, liegt in der Vorsicht, kein schlechtes Design in der Sprache zu verankern, und im Problem der "alignment in attention"
- Das Rust-Projekt wird von Freiwilligen getragen; wenn sich niemand gezielt auf eine bestimmte Funktion konzentriert, verzögert sich die Entwicklung
- Das Projekt Rust for Linux ist ein Thema, das viele Menschen begeistert, und hilft deshalb dabei, die Anstrengungen auf einige für den Kernel notwendige Kernfunktionen zu bündeln
- Rust-Sprachteam-Co-Leiter Tyler Mandry stellte auf der Kangrejos 2025 kommende Sprachfunktionen von Rust vor und erwähnte, dass das Linux-Kernel-Projekt als Katalysator für die Weiterentwicklung von Rust gewirkt habe
- Wichtige Funktionen: Field Projection, In-place Initialization, Arbitrary Self Types
- Die Kernel-Entwicklung benennt konkrete Anwendungsfälle und technische Anforderungen klar und trägt so dazu bei, die Richtung des Sprachdesigns von Rust zu konkretisieren
- Höchste Priorität hat die Standardisierung von instabilen (unstable) Funktionen, die in Kernel-Bindings bereits verwendet werden
Field Projection
- Eine Funktion zum Extrahieren eines Zeigers auf ein bestimmtes Feld aus einem Struct-Zeiger; sie ist der Versuch, den C-Ausdruck
&(r->field) in Rust zu verallgemeinern
- Bisher war dies nur bei Referenzen (
&) und Zeigern (*mut) möglich, bei benutzerdefinierten Smart Pointern gab es jedoch Einschränkungen
- Rust for Linux arbeitet daran, dies zu erweitern, sodass der Zugriff auf Felder bei allen Zeigertypen mit derselben Syntax möglich wird
- Insbesondere beim Umgang mit dem Typ Pin (nicht verschiebbare Strukturen) ist vorgesehen, dass bei der Feldprojektion automatisch in
Pin<&mut Field> oder &mut Field umgewandelt wird
- Wenn diese Funktion implementiert wird, kann das RCU-(Read-Copy-Update)-Muster in Rust sicher unterstützt werden, sodass leistungsstarker Datenzugriff auch ohne Lock möglich wird
- Sie wird derzeit im GitHub-tracking issue diskutiert, mit dem Ziel einer Stabilisierung vor Debian 14 (2027)
Arbitrary Self Types
- Eine Funktion, die Methodendefinitionen ermöglicht, die Smart Pointer akzeptieren
- Bisher wurde nur die Form
fn method(&self) unterstützt, künftig sind auch Formen wie fn method(self: Pin<&mut MyStruct>) möglich
- Im Kernel ist dies unverzichtbar, da dort verschiedene Pointer-Wrapper wie Arc, Pin und Mutex verwendet werden
- Bei der Implementierung gab es Konflikte mit dem Deref-Trait, die jedoch durch die Einführung eines neuen Receiver-Traits gelöst werden sollen
- Receiver dient dazu, explizit festzulegen, dass ein Pointer als Arbitrary Self Type verwendet werden kann
- In der Kernel-Entwicklung lassen sich damit Aufrufe über Pointer-Ketten kompakt halten
- Ding prüft mit dem Tool Crater derzeit die Kompatibilität mit bestehenden Rust-Paketen und erwähnte eine mögliche Stabilisierung innerhalb eines Jahres
In-place Initialization
- Eine Funktion, die das im Kernel verwendete Makro
pin_init!() auf Sprachebene unterstützt
- Sie ermöglicht, Objekte nach ihrer Erzeugung nicht zu verschieben, sondern direkt im Speicher zu initialisieren, was für Pin-Structs, Future und dyn-Traits nützlich ist
- Drei Vorschläge werden parallel diskutiert
- init-Keyword-Ansatz: minimale Syntaxerweiterung unter Nutzung des bestehenden
PinInit-Traits
&out-Referenzansatz: ergänzt eine schreibgeschützte Referenz nach Art eines C-out-Pointers und unterstützt Initialisierung auf Feldebene
- C++-artiger Optimierungsansatz: Objekte, die sofort auf den Heap verschoben würden, werden von Anfang an direkt auf dem Heap erzeugt
- Am Ende ist geplant, sowohl den PinInit- als auch den Out-Reference-Ansatz experimentell zu erproben, um die tatsächliche Nutzbarkeit zu prüfen
- Mit der Einführung dieser Funktion dürfte nicht nur der Kernel, sondern auch die Struktur asynchronen Rust-Codes insgesamt vereinfacht werden
Einfluss von Rust for Linux auf Rust
- Der Linux-Kernel dient bei der Weiterentwicklung der Sprache Rust als realistisches Testbed
- Alle drei Funktionen gingen von kerneltypischen Anforderungen wie Smart Pointern, fest angeheftetem Speicher und Nebenläufigkeitsstrukturen aus,
letztlich werden aber auch allgemeine Rust-Entwickler davon profitieren
- Diese Veränderungen gelten als Beispiel für einen positiven Rückkopplungseffekt zwischen Kernel- und Sprachentwicklung
2 Kommentare
Hacker-News-Kommentare
Es hat eine Weile gedauert, das RFC-Dokument zu Rusts Feature für lightweight clones zu lesen und zu verstehen. Anfangs fand ich dieses Feature ziemlich interessant, aber am Ende hatte ich erneut den Eindruck, dass Rust eine schwer zu erlernende Sprache ist. Aus Sicht von jemandem wie mir, der weder Rust noch C++ wirklich gründlich gelernt hat, wirkt es wie eine Kombination aller Konzepte und Features von modernem C++ mit Elementen aus Haskell. Trotzdem habe ich das Gefühl, dass an Rust etwas dran sein muss, wenn man sieht, wie viele nützliche Dinge Leute damit bauen. Deshalb habe ich vor, Rust bald noch einmal ernsthaft auszuprobieren. Passender RFC-Link
Meiner Erfahrung nach ist C++ viel komplizierter als Rust. Es gibt zum Beispiel allein acht Arten der Initialisierung, fünf Arten von Werten einschließlich xvalues, inkonsistente Formatierungs- und Naming-Conventions, die rule of 5, Exception-Handling, die Notwendigkeit, bei move assignment immer
this != otherzu prüfen, perfect forwarding, SFINAE, Probleme mit Trait-Ersatzkonstruktionen und noch viele weitere komplizierte Punkte. Um C++ richtig zu verwenden, muss man zusätzlich zu den Sprachstandards auch die darauf aufbauenden Konventionen lernen, die sicheres und schnelles Programmieren ermöglichen. Außerdem kollidieren Methoden wie Exception-Handling oft miteinander oder zwingen einen zu suboptimalen AnsätzenIn der Rust-Community wird dieser Vorschlag für lightweight clones zunehmend negativ gesehen. Der Grund ist, dass er zu komplex ist. Auch im offiziellen Rust-Dokument zur Designrichtung ist Komplexität das größte Hindernis. Nicht jedes vorgeschlagene Feature in Rust muss unbedingt einfach sein, aber lightweight clone macht im Grunde nur etwas bereits Mögliches ein wenig leichter. Wenn Leute es dann nicht leicht verstehen, ist das ein Problem
Aus der Distanz wirkt Rust riesig, aber wenn man tatsächlich damit programmiert, gewöhnt man sich schnell daran. Ich habe zum Beispiel anfangs das Konzept von lifetimes überhaupt nicht verstanden und nur mit
Rc<>programmiert. Nachdem ich die Grundlagen gelernt hatte und mich dann noch einmal mit lifetimes beschäftigt habe, war es deutlich einfacher. Tatsächlich müssen sich die meisten Nutzer mit Features wie lightweight clones gar nicht befassenIm Vergleich zu C ist es sehr komplex, aber im Vergleich zu C++ eine viel einfachere Sprache. Es ist fast nie nötig, in Dokumentation oder Referenzwerken nachzuschlagen, nur um Code zu verstehen. Rust sitzt in einer sehr guten Mitte: „nicht so komplex, dass einem die Haare ausfallen, und nicht so einfach, dass der Code kompliziert werden muss“. Außerdem hat der Code standardmäßig correctness
Die Komplexität von Rust führt viel seltener als bei C++ dazu, dass man versehentlich etwas falsch benutzt. Und wenn man clippy verwendet, wird der Code automatisch in idiomatischere Formen umgewandelt, sodass man auch auf neue Features wie lightweight clones hingewiesen wird, selbst wenn man sie gar nicht kennt. Das meiste lässt sich leicht nur mit der
--fix-Option von clippy anwenden. Genau das unterscheidet Rust entscheidend von C++, wo sich ungenutzte Features ansammeln oder Dinge nur übermäßig kompliziert verwendet werden und dadurch die Komplexität immer weiter anwächstIch hatte gelesen, dass das Rust-for-Linux-Projekt Rust stark geholfen habe, und habe deshalb aus Neugier im Kernel-Tree nach
*.rs-Dateien gesucht. Dabei sah ich, dass es unter dem Rust-Ordner eine API-Kompatibilitätsschicht gibt und im Kernel-Tree nur ein paar Proof-of-Concept-Treiber, die bestehende Treiber einfach neu schreiben, vorhanden sind (abgesehen vom unvollständigen Nvidia-Treiber). Es wirkt, als gäbe es praktisch nichts, was wirklich im Einsatz ist. Auch das Android-Binder-Rewrite in Rust scheint nur gerade so weiterzuleben. Insgesamt wirkt das auf mich eher wie ein niedliches Experimentierprojekt, und ich frage mich, ob man in dem Source-Tree der wichtigsten Software der Welt wirklich solche gemeinsamen Sprachexperimente machen muss. Wäre es nicht besser, so etwas in einem experimentelleren OS wie Redox zu tun?Der GPU-Treiber für Apple Silicon wurde in Rust geschrieben, und der Autor selbst sagt, dass die Entwicklung in C viel schwieriger gewesen wäre. Er ist zwar noch nicht upstream, aber: „Wenn man einen komplexen neuen Kernel-Treiber baut, treten normalerweise alle möglichen Probleme wie race conditions, memory leaks und use-after-free auf. In Rust geschrieben läuft er dagegen fast ohne solche Probleme stabil. Dank der Sicherheitsfeatures habe ich die Gewissheit, dass der Treiber thread-safe und memory-safe ist, und auch das Design entwickelt sich auf natürliche Weise in eine gute Richtung. Das ist für mich die Magie von Rust.“ Link zum Erfahrungsbericht des Autors Und zu der Frage, ob man solche Experimente im Kernel-Tree machen sollte, wurde erwähnt, dass Torvalds dem nicht zustimmt
Die Ansicht, das „Android-Binder-Rewrite in Rust lebe nur noch gerade so“, stimmt nicht. Dieses Projekt soll die aktuelle C-Implementierung vollständig ersetzen. Passender Artikel Auch wichtige Treiber für Apple-Hardware der M-Serie sind in Rust geschrieben und sind keine bloßen Rewrites oder Proofs of Concept
Um komplexe Treiber zu bauen, braucht man zunächst eine Interfaceschicht. Das RfL-Projekt arbeitet daran, genau diese Infrastrukturschicht upstream hinzuzufügen, und erst wenn diese Grundlage steht, lassen sich auch komplexe Treiber schreiben. Bei Red Hat wird an nova gearbeitet, bei asahi an der Apple-GPU und bei Collabora an ARM Mali. Wenn selbst drei GPU-Treiber nicht als komplexe Treiber gelten, was dann?
Zu der Behauptung, das Rust-Binder-Rewrite würde nur noch so vor sich hin leben: Wenn man sich die Commit-Messages im Linus-Tree ansieht, ist der Reifegrad ziemlich hoch. Rust Binder besteht alle Binder-Tests des Android Open Source Project, und verschiedene Apps und Funktionen laufen ohne nennenswerte Probleme. Auf dem cuttlefish-Emulator und dem Pixel 6 Pro gibt es ebenfalls keine Probleme beim Booten oder Ausführen von Apps. Der aktuelle Funktionsumfang ist derselbe wie beim C-Binder, nur einige Debugging-Features fehlen noch und sollen bald ergänzt werden. Außerdem begann das Rust-for-Linux-Projekt zwar ursprünglich außerhalb des Kernel-Tree, aber wenn man reale Integration erproben will, muss man letztlich im Tree selbst experimentieren
Das Ziel dieses Projekts ist kein gemeinsames Sprachexperiment in der Entwicklung, sondern Rust für die Linux-Kernel-Entwicklung nutzbar zu machen. Das Projekt wurde von Kernentwicklern direkt gestartet, weil sie Rust verwenden wollten. Gerade weil es sich um so wichtige Software handelt, geht man vorsichtig vor, und der Aufbau der Grundlagen braucht Zeit. Dass Rust durch dieses Projekt zusätzlich profitiert, ist nur ein Nebeneffekt
Im <i>Abschnitt zu field projection stand im Artikel, dass entschieden wurde, dass jetzt alle Struct-Felder strukturell gepinnt sind und daher immer Typen wie
Pin<&mut Field>herauskommen</i>, und ich hatte diesen Punkt zuvor übersehen. Technisch ist das komplex, aber ich freue mich, dass diese Entscheidung ein Problem gelöst hat, das viele Diskussionen blockiert hatteÜber die <i>endgültige Designidee, inspiriert von C++, eine erwartete Optimierung zu verwenden, bei der ein neuer Wert von Anfang an auf dem Heap erzeugt wird, wenn man ihn erstellt und sofort verschiebt, um ihn auf dem Heap zu allokieren</i>, wird derzeit diskutiert. Die Bezeichnung „Optimierung“ ist hier irreführend, deshalb gibt es Diskussionen über eine Umbenennung
Vielleicht verstehe ich die Komplexität des Problems nicht vollständig, aber ließe sich das nicht einfach mit einer passenden calling convention lösen? Wenn eine Struct größer als x ist oder einen marker type hat, übergibt der caller einen Puffer per outref, und der callee schreibt die Struct direkt in diesen Puffer. So könnte man auch bei normalem Code doppelte Heap-Allokationen vermeiden, und in anderen Situationen würden unnötige Kopien ebenfalls reduziert. Da in dieses Thema bereits viel Arbeit geflossen ist, würde mich eher interessieren, warum die vorgeschlagenen Lösungen unbequemer sind
Ich denke, es wäre für alle intuitiver, so etwas nicht als interne Optimierung zu behandeln, sondern mit einer Funktion wie
newexplizit zu machenIn C++ nennt man diese Semantik elision
Wie wäre es mit einem Namen wie „coalesced heap construction“ oder „coalesced heap allocation“?
Immer wenn ich von verschiedenen Rust-Features höre, mache ich den Witz: „Lasst uns nur bloß nichts wie tokio in den Kernel packen.“ Wenn Rust weit genug reift und ein direct composition renderer auftaucht, sodass Apps möglich werden, die über den ganzen Kernel hinweg laufen, könnte das allerdings auch wieder eine ziemlich interessante Situation sein
Einen Async-Runtime im Kernel zu implementieren ist wirklich trivial. Eigentlich ist schon die Workqueue des Kernels selbst eine Runtime
Falls das nicht als Witz gemeint war, dann ist das ein grundlegendes Missverständnis darüber, wie Kernel-Code in Rust (und C) aufgebaut ist. Beim Programmieren für den Kernel läuft Rust wie C ohne Standardbibliothek, also
no_std, und man kann weder Cargo noch Crates verwenden. Um tokio zu nutzen, müsste man die Hälfte davon neu schreiben und alle Interaktionen mit dem OS wie Sockets durch Kernel-Mechanismen ersetzenIch denke, diese aktuellen Rust-Features für Linux sind nicht nur für den Kernel relevant, sondern gehören zu den ersten Features, die Rust allgemein breit nützen. Es wirkt auf mich ein wenig so, als habe die stark kernelzentrierte Feature-Entwicklung andere Sprach- oder Library-Features etwas ausgebremst
Soweit ich weiß, ist systems programming das wichtigste Einsatzfeld von Rust. Interoperabilität ist entscheidend in Bereichen wie OS, Embedded oder komplexen C-basierten Systemen. Diese Features wirken tatsächlich nicht kernspezifisch, sondern eher wie allgemeine Utilities, die für reales Systems Programming unbedingt nötig sind
Weiterentwicklungen für Kernel- und Firmware-Entwicklung finden an vielen Stellen statt, stehen aber nicht immer im Mittelpunkt des allgemeinen Interesses. Philipp treibt gerade in diesem Bereich besonders große Veränderungen voran
Die Hauptrolle von Rust liegt im low-level Systems Programming. In anderen Bereichen sind kompilierte Managed Languages im Userspace oft die deutlich bessere Wahl, und in Systemen nach dem Vorbild von Self, Inferno oder der Android-Architektur ist es meiner Meinung nach kein Problem, sich auf solche low-level Features im C-Stil zu konzentrieren
Diese Features sind großartig genug, um nicht nur dem Kernel, sondern auch anderen Bereichen zu helfen (vor allem generalized projections). Es freut mich sehr, dass Linux die Weiterentwicklung der Sprache Rust vorantreibt
Ich frage mich, ob es Forschung oder Tools gibt, die mit LLMs automatisch C-Code in Rust umwandeln. Ob man ein LLM dazu bringen könnte, Rust-Code für chat-, usb-, i2c- oder GPU-Treiber zu erzeugen und ihn bis zum Build und Test durchlaufen zu lassen, oder ob man das auch mit „kleineren“ Projekten wie sqlite, apache oder nginx ausprobieren könnte
Als nicht auf LLMs basierendes Forschungsprojekt gibt es das c2rust-Projekt, das bereits in realen Fällen eingesetzt wurde. LLMs bleiben grundsätzlich bei Annäherungen, liefern also keine ausreichende Genauigkeit und produzieren zu viele subtile Bugs. Ohne eine Kombination mit formalen Methoden ist das nicht praktikabel. Viele Probleme werden auch durch Unit-Tests nicht entdeckt. Und die als „kleiner“ genannten Projekte sind in Wirklichkeit ebenfalls nicht wirklich klein. Siehe auch reale Einsatzbeispiele
Auch Darpa interessiert sich für solche Forschung und hat dafür Fördermittel bereitgestellt, aber die Ergebnisse sind noch nicht in einem Stadium, in dem es schon konkrete Resultate gibt
Fanatische Ausrichtung (alignment in dogmatism)