My Rust programming language notes

New 29Apr2024. Updated 07Nov2024 (New style of References. [8] and those below in the list++. In work. This page is in group Technology I guess it’s a post where I try to understand its process/task model, and end up being somewhat sceptical of any potential usage on my behalf.

Three rusts

«Rust has been said to be named after a particularly robust type of fungi that is “over-engineered for survival” according to Hoare.» from [1]. «Rust began as a personal project in 2006 by Mozilla Research employee Graydon Hoare.» (Wikipedia)

Fig.1 – Rusted stud link chain

It’s probably the (1) rust fungus (here) Hoare is referring to. Why not start with an aside: the (2) programming language name «Rust» probably does not have any double meaning with the (3) iron oxide type of rust (here), which for many years has had its way with this fascinating stud link chain that I found on a dock facility. It is often used for anchoring ships since this type (also) is rather over-engineered for rough usage (survival).

Aside: The story of when I found this chain is at Øyvind Teig [Rusted chain].

Pike’s async/await lament

This note started with an attempt to explain Rob Pike‘s subtle lament over async/await in a lecture in 2023. Here is my summary, moved from another note and edited. Over the time I have added so much that I found it difficult to differentiate Pike’s laments from mine, even if the red numbers should signify some origin from Pike. But I guess you’d have to listen to Pike first.

(1) Pike talks in general terms here. Explained, it might mean something like the red points below, even he does not mention Rust with a word:
(2) Rust does have (expensive?) native OS thread (here) (scheduled by the OS) and channel (here) (asynchronous channels for communication between threads).
(3) For lighter threads the Rust team has chosen the async/await paradigm (Async/await on Wikipedia «Rust added support for async/await with version 1.39.0 in 2019 using the async keyword and the .await postfix operator, both introduced in the 2018 edition of the language» and async-await on the Rust Blog [6]) where an async_fn «task» function returns a Future which eventually delivers a value via a cancelable poll, by an executor, (not callbacks) – where the future is passed between an executor and a reactor until (puh!) the future resolves and finally is seen by thefuture.await (syntax: instead of other languages’ await future which by the way also seems to be the syntax in Rust in 2019 [7], which is where I also got these details from). (A future is called a promise in JavaScript.) Central to this is the tokio runtime as #[tokio::main] (at least suitable for backend usage since it seems to require concurrency)
(4) One may also use other runtimes, like Rayon, reqwest or Embassy (the latter suitable for embedded applications). These runtimes seem to be exclusive to each other. See Default runtime? [12] and a list of suggested ones [13].
(5) I hope, once any one of us have chosen (only?) one of these, that this is still supported some years away. And that the next programmer reading my code will understand and agree. Rust probably then is not an instance of occam’s razor, saying that «Entities must not be multiplied beyond necessity». (Maybe I’m just saying this because I’m silently knocking at the door, with another type of baggage; then everything looks more complicated than «necessary»? Maybe by «Blub experience» hinders me to dare? Read from «The Blub Paradox» in [2])
(6) Why didn’t they choose CSP (072:[«Bell Labs and CSP Threads»]) with asynchronous channels and select? I just wonder, as Rob Pike did, as «it pushes some of the complexity back on the programmer«. Even if he suggest why this might be so (it «requires significant runtime complexity, and I can understand why some folks prefer not to build that into their systems«).
(6b) I might have found the source for this at the lecture from 2019 [6] (@9.24) where withoutboats says the Rust team removed «green threads» (as he says Go’s goroutines are) because they weren’t zero cost since «it was imposing costs on people who didn’t need it«. Firstly he doesn’t mention the Go mostly-synchronous channels, which probably should have been mentioned on the out-breath, because these are so intermingled and important for any comparison. «Green threads introduce a runtime which all Rust programs must use, even if they don’t need the feature«. I can understand this from a language designer’s standpoint, but not from my standpoint. Since I would need it, would it then have had zero cost?
(7) But then, not for small embedded. I have written two runtimes for light-weight CSP (or «CSP») tasks in C. It’s not that difficult. But this was on top of nothing, just running on the iron (ChanSched, also a second author on this). However, the first runtime was on top of an existing SDL runtime system, with asynchronous buffered messages underneath (CHAN_CSP). But the latter was more difficult to read than the first. Of course C allows that burden, but the weight is carried by the user
(8) These points are also questions which I’d like to know the answers to myself, since I, to be honest, don’t understand all of this
(9) I think Turing postulated that the Turing Machine may simulate any other computer. Would this imply that one could make a CSP runtime for light threads, or even for parallel tasks, for Rust? Or is this only revealing that I long for my golden hammer and don’t want a difficult saw to quarrel with?
(10) Now read about why the async/await model seems to be tuned to web services, not to «my» need of a task/process model, which is the CSP type task, as seen in languages like Go, occam and xC. Or to «others» task/process model, which often is the thread model. [8]. I find this strange, since they also call it a systems programming language. I therefore am in doubt whether I should proceed into Rust any further. Also the «zero-cost» stuff only attracts me only so much [9], which some times seems to violate «the principle of least surprise» [11]. However, I am still curious, even if «After all, asynchronous programming is more challenging than just using threads» [11].
(11) Maybe my piece of cake should be to look more into channel and select! After all, that was my initial reason for lifting my eye brows. Because they do have two terms here: asynchronous Rust and blocking Rust [11].
(12) «Note that blocking is an async function.» Believe it or not. Like instd::async::blocking(|| ...).await; [13]

Misc

«Rust uses stackless coroutines to model asynchronous operations«. From p135 of [3] (Samson)

From p27 of the same I read:

  1. Stackful: Each task has its own call stack. This is often implemented as a stack that’s similar to the stack used by the operating system for its threads. Stackful tasks can suspend execution at any point in the program as the whole stack is preserved.
  2. Stackless: There is not a separate stack for each task; they all run sharing the same call stack. A task can’t be suspended in the middle of a stack frame, limiting the runtime’s ability to pre-empt the task. However, they need to store/restore less information when switching between tasks so they can be more efficient.
There are more nuances to these two categories that you’ll get a deep understanding of when we implement an example of both a stackful coroutine (fiber) and a stackless coroutine (Rust futures generated by async/await) later in the book. For now, we keep the details to a minimum to just provide an overview.

Samson also points out (on p.33) that «Goroutines is an example of a specific implementation of stackfull coroutines..» (Like for the Go language, or golang). And on p133: «tasks (stackless coroutines to be precise)«.

An aside that I need to try to put Rust in context in my own head. It may or may not be interesting to you: The runtimes mentioned above that I have written, plus the Southampton Portable occam Compiler (SPoC) in the nineties which generated C from occam sources all managed without separate stacks, ie. were stackless. Since occam could communicate over its synchronous channels also from tasks that were started from tasks (task = PROCesses), the C code from SPoC plus its runtime flattened out the hierarchy of tasks to make them not detect that they were on top of each other. This way when the only return (from scheduling points) that there were, would only be a simple C return from the task down to the scheduler. Exactly like in my runtimes. However, each process would have its own context where local state was stored. The pointers to these data structures (stored on the heap) would be given to the tasks on each «call» or scheduling. These all are based on cooperative scheduling and non-preemptive multitasking. We wrote quite many safety-critical applications with these concepts. An even older scheduler I wrote (here) I have later discovered has a concurrency model that isn’t far from the one in JavaScript (JS). Another quite orthogonal architecture (but based on the same ideas) is the super-fast scheme that XMOS xC (or lib_xcore) represent (My XMOS pages).

I think the above schemes are much related to the Rust coroutine’s yield or preemption points. For one of my schedulers I did have to add a yield in addition to the synchronisation points.

A popular language

I read more and more student reports and degrees discussing Rust. Plus I hear from the inside of companies that they are starting to use Rust, also for somewhat «higher level» embedded.

Mark Russinovich wrote on X (then Twitter) that (for Microsoft Windows at least (I guess), since he’s the CTO of Microsoft Azure and I think, the initiator of Sysinternals) [4]:

Speaking of languages, it’s time to halt starting any new projects in C/C++ and use Rust for those scenarios where a non-GC language is required. For the sake of security and reliability. the industry should declare those languages as deprecated.

Of course that statement is crushed in the comment field, but anyhow. I just wonder how this stands now on 11Aug2024, soon two years after.

Safety critical?

From the comments I did find that a company called ferrocene is making a toolchain meant to make Rust systems certifiable [5]. They say that their Rust compiler is «ISO26262 (ASIL D) and IEC 61508 (SIL 4) available for x86 and ARM platforms«. How this is used when a company wants to get a SIL 3 approval for field installations of a whole product I’d certainly like to know. The fact that a compiler for any language is certified doesn’t automatically make the target language itself certified. The compiler could be written in (subsetted and ruled?) C, the compiled language could be Rust. For the target language, subsets and rules might apply. I remember the Ada Ravenscar profile where the language was was subset’ed so much that the real good stuff was deliberately set aside to make it hard real-time and safety critical in that domain (Wiki-refs). Non-deterministic features were placed in the back seat, to say it mildly. Needing, like No_Select_Statements to get rid of the beautiful Ada select statements. I don’t know if hard real-time systems is what ferrocene is aiming for. From the ferrocene blog (here, by ferrous systems) I read that «This will bring long-awaited features like the C-unwind ABI and async functions and returning imply Trait in traits to safety-critical users.» Which I think implies that async functions could not, up to this time, for systems written in the target Rust language, be considered for certification if the users had used async functions. (Big question mark here.) I probably have too many balls in the air now, but this is an area where juggling is the art. Even before the person from TÜV enters the room.

Scratchpad

Embedded

It seems like to run on any embedded board there are two projects. (1) to get Rust up and running and then later on perhaps (2).. But then perhaps there are ready solutions out there?

Operating System development tutorials in Rust on the Raspberry Pi

https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials

«This is a tutorial series for hobby OS developers who are new to ARM’s 64 bit ARMv8-A architecture. The tutorials will give a guided, step-by-step tour of how to write a monolithic Operating System kernel for an embedded system from scratch. They cover implementation of common Operating Systems tasks, like writing to the serial console, setting up virtual memory and handling HW exceptions. All while leveraging Rust’s unique features to provide for safety and speed.» Andre (@andre-richter)

A complete guide to running Rust on Arduino

https://blog.logrocket.com/complete-guide-running-rust-arduino/ by MacBobby Chibuzor

Brit chip company picks RISC-V for next-gen microcontrollers

Also see My XMOS pages

References

Wiki-refs: Ravenscar profile, Rust (programming language)

[1] Why Rust is the most admired language among developers by Sara Verdi, 30Aug2023, see https://github.blog/2023-08-30-why-rust-is-the-most-admired-language-among-developers/

[2] Beating the Averages by Paul Graham (April 2001, rev. April 2003), see https://www.paulgraham.com/avg.html

[3] Asynchronous Programming in Rust: Learn asynchronous programming by building working examples of futures, green threads, and runtimes by Carl Fredrik Samson. See https://www.packtpub.com/en-no/product/asynchronous-programming-in-rust-9781805128137

[4] Mark Russinovich, 20Sep2022. Read at https://x.com/markrussinovich/status/1571995117233504257

[5] This is Rust for critical systems. By ferrocene. Read at https://ferrocene.dev/en/. Ferrocene is qualified and maintained by Ferrous Systems, see https://ferrous-systems.com

[6] Async-await on stable Rust! Nov. 7, 2019 by Niko Matsakis on Rust Blog at https://blog.rust-lang.org/2019/11/07/Async-await-stable.html

[7] RustLatam 2019 – Without Boats: Zero-Cost Async IO at (RustLatam 2019) by withoutboats. See @9.24: https://www.youtube.com/watch?v=skos4B5x7qE. The postfix operator is not used yet, so he usesawait(something) instead of the newer something.await, since the syntax was to be stabilised some time later in 2019 (@29.59). Also in the future they wanted to make generators that yield (like in Python and JavaScript)

[8] Avoiding async entirely #58, started by John-Nagle on Mar2021. See https://github.com/rust-lang/wg-async/issues/58

[9] async under the hood, is it zero-cost? started by _icsi_ on Aug2023. See https://www.reddit.com/r/rust/comments/12c9ld0/async_under_the_hood_is_it_zerocost/

[10] Major unresolved questions or controversies: To await or not to await? See https://rust-lang.github.io/wg-async/vision/unresolved_questions/await_or_not.html

[11] EXPLORING WAYS TO MAKE ASYNC RUST EASIER by Carl Lerche (Jun2021)https://carllerche.com/2021/06/17/six-ways-to-make-async-rust-easier/. He also reasons for removing .await from Rust

[12] Major unresolved questions or controversies: Default runtime? See https://rust-lang.github.io/wg-async/vision/unresolved_questions/default_runtime.html

[13] User’s Manual of the Future. See https://rust-lang.github.io/wg-async/vision/shiny_future/users_manual.html

Leave a Reply

Dette nettstedet bruker Akismet for å redusere spam. Lær om hvordan dine kommentar-data prosesseres.