ST startet eine Initiative, um Rust-Treiber für viele unserer Sensoren bereitzustellen, und eröffnet damit einen neuen Weg für alle, die diese Programmiersprache in ihrer Embedded-Entwicklung einsetzen möchten. Selbstverständlich werden wir unser C-basiertes Ökosystem weiterhin ausliefern und pflegen. Wir sind uns jedoch auch der wachsenden Beliebtheit der Programmiersprache Rust bewusst, der Vorteile, die sie für viele Projekte bietet, und der Tatsache, dass der Aufbau einer robusten, plattformunabhängigen Alternative in einer anderen Programmiersprache erhebliche Zeit und Investitionen erfordert. Aus diesen Gründen haben wir damit begonnen, Treiber Schritt für Schritt zu portieren, machen jetzt auf unsere Präsenz auf crates.io aufmerksam und stellen Projekte wie Embassy STM32 vor, das eine Hardware-Abstraktionsschicht in Rust für unsere MCUs bereitstellt.
Grund Nr. 1: Sicherheit

Einer der beliebtesten Aspekte von Rust ist die inhärente Codesicherheit im Vergleich zu anderen Sprachen, einschließlich C und C++. Im Kern bedeutet das, dass der Rust-Compiler bestimmte Operationen verbietet, die die Speichersicherheit gefährden könnten. Speichersicher zu sein bedeutet, dass der Rust-Compiler jeden Speicherzugriff blockiert, wenn der Speicher nicht explizit allokiert oder bereits wieder freigegeben wurde. Das ist ein deutlicher Unterschied zu C und C++, wo Entwickler bei der Speicherverwaltung wesentlich mehr Freiheiten haben. Es bedeutet aber auch, dass fehlerhafter Code zu Problemen wie Pufferüberläufen, Speicherlecks sowie „Use-after-free“ oder hängenden Zeigern (Dangling Pointers) führen kann. All das kann Datenbeschädigungen oder sogar Sicherheitslücken zur Folge haben.
Der Grund, warum Rust sicherer ist, liegt darin, dass es das Prinzip „Resource acquisition is initialization“ (RAII) umsetzt: eine Technik, bei der Ressourcen beim Erzeugen eines Objekts automatisch und sofort allokiert und beim Löschen des Objekts wieder freigegeben werden. Der Compiler weist diese Ressourcen einem Besitzer (Owner) zu und kann die Besitzrechte anschließend übertragen oder bei Bedarf einem anderen Besitzer erlauben, sie auszuleihen. Dadurch übernimmt Rust die Ressourcenverwaltung selbst und benötigt keinen Garbage Collector zur Verwaltung von Speicherallokationen, anders als C++, eine weitere Sprache, die RAII umsetzt, bei der die Entwickler den Speicher aber weiterhin selbst verwalten müssen. In der Praxis reduziert das Fehlen eines Garbage Collectors den Overhead, macht Rust-Code aber auch deutlich weniger nachsichtig.
Dieser Blogbeitrag kann nicht einmal ansatzweise alle Unterschiede zwischen Rust und C bzw. C++ behandeln (auf die Typsicherheit gehen wir gar nicht erst ein), doch der oben genannte Punkt verdeutlicht einen wichtigen Grund, warum Entwickler zu Rust wechseln und warum ST bereits versucht hat, Alternativen zu seinen Sensortreibern anzubieten. Die Lernkurve von Rust ist steil. Sicheren Code zu schreiben, der kompiliert, ist anspruchsvoll, weil es so viele Leitplanken gibt, die die Codesicherheit gewährleisten. Es bedeutet aber auch, dass Entwickler, sobald der Code kompiliert, eine weitaus größere Gewissheit in Bezug auf Sicherheit haben. Und für Entwickler, die neu in Rust sind, bedeuten Open-Source-Treiber wie die von ST bereitgestellten, dass sie unsere Implementierungen studieren und von unseren Erfahrungen lernen können.
Grund Nr. 2: Praxistauglichkeit, Portabilität und Performance (mit Einschränkungen)

Ein weiterer Grund, warum Entwickler Rust für ihre Embedded-Projekte einsetzen, ist die Art und Weise, wie es Schnittstellen und Busoperationen handhabt. Anders als C, das traditionell eine zeigerbasierte Busabstraktion verwendet, nutzt Rust „Traits“, die Schnittstellen als Typen definieren und darüber eine flexible Abstraktion bereitstellen. Über Ökosystem-Standards wie embedded-HAL lassen sich Schnittstellen für Busse wie I2C und SPI plattformübergreifend einheitlich implementieren, was Entwicklern hilft, Know-how aufzubauen. Tatsächlich verwendet unser Code bereits APIs, die an unsere ursprünglichen C-Treiber angelehnt sind, um die Lernkurve abzuflachen, und wir haben eine einheitliche Namensgebung gewählt, um den Umstieg auf unsere Rust-Alternative zu erleichtern.
Ein Irrtum, dem wir häufig begegnen, ist die Annahme, Rust sei von Natur aus „schneller“ als C oder C++. Die Wahrheit ist weitaus differenzierter. Je nach Anwendung kann Rust langsamer, gleich schnell oder schneller als C sein. In vielen Fällen zwingt Rust Teams jedoch dazu, ihre Anwendung neu zu schreiben, was ganz natürlich zu Refactoring, zum Verzicht auf Altcode oder zur Schaffung neuer, optimierter Abläufe führt. Das Fehlen eines Garbage Collectors in Rust oder die Verbesserungen seines modernen Compilers helfen sicherlich, aber Embedded-Entwickler sollten nicht überstürzt davon ausgehen, dass Rust wie von Zauberhand deutliche Laufzeitverbesserungen liefert. Es bietet jedoch deutlich sichereren und oft flexibleren Code, und genau deshalb stellen wir Rust-Alternativen zu unseren C-Sensortreibern bereit.
Grund Nr. 3: Die Rust-Community

Die Diskussionen in unserem GitHub-Repository zu diesem Projekt zeigen eine lebendige Community, was für viele Entwickler ein wichtiger Faktor ist. Es sind nicht nur zentrale Branchenakteure wie Meta, Google oder Microsoft, die sich öffentlich zur Einführung der neuen Programmiersprache bekannt haben. Viele kleinere Teams investieren in die neue Sprache, weil es Zeit braucht, sie sicher zu beherrschen, und weil ein Rückstand zu erheblichen technischen Defiziten führen könnte, von denen ein Wettbewerber profitieren würde. Wir wissen, dass sich die Einführung noch in einer frühen Phase befindet. Genau deshalb stellen wir jetzt Rust-Sensortreiber bereit. ST möchte diejenigen unterstützen, die bei diesem Wandel vorne mit dabei sein wollen, einem Wandel, der die Zukunft der Embedded-Entwicklung zu prägen scheint.
Der beste Einstieg ist, Rust mit rustup zu installieren und sich dann, während das Projekt entsteht, eines unserer Pakete auf Crates zu holen. Unsere GitHub-Seite führt die Nutzer zudem durch den Download- und Integrationsprozess. Wir bieten Beispiele in Cargo an, damit Teams schneller zu Ergebnissen kommen. Und während die Treiber selbst es Ingenieuren ermöglichen, MCU-unabhängig zu arbeiten, sind die von uns verwendeten Beispiele für STM32-Mikrocontroller geschrieben und nutzen in erster Linie das Entwicklungsboard NUCLEO-F401RE. Die aktuellen Rust-Treiber konzentrieren sich auf die Schnittstellen I2C und SPI, da diese mit Abstand am häufigsten verwendet werden; weitere Unterstützung, einschließlich I3C, ist jedoch in Arbeit, und wir werden diesen Blogbeitrag aktualisieren, sobald wir größere Updates veröffentlichen.
