LWN: Comments on "Resources for learning Rust for kernel development" https://lwn.net/Articles/990619/ This is a special feed containing comments posted to the individual LWN article titled "Resources for learning Rust for kernel development". en-us Fri, 17 Oct 2025 10:04:51 +0000 Fri, 17 Oct 2025 10:04:51 +0000 https://www.rssboard.org/rss-specification lwn@lwn.net Localised bugs https://lwn.net/Articles/993972/ https://lwn.net/Articles/993972/ sammythesnake <div class="FormattedComment"> I think AJ Rimmer's defence council described anyone who would craft such a situation as "a yogurt"...<br> </div> Mon, 14 Oct 2024 09:52:10 +0000 Locking https://lwn.net/Articles/993021/ https://lwn.net/Articles/993021/ slanterns <div class="FormattedComment"> <span class="QuotedText">&gt; The lack of "unlock" has led to proposals to give Rust's Mutex&lt;T&gt; type an unlock method.</span><br> <p> <a href="https://github.com/rust-lang/rust/issues/81872#issuecomment-1440340669">https://github.com/rust-lang/rust/issues/81872#issuecomme...</a><br> <p> It was once added to the standard library as an experiment, and later withdrawn since the libs-api team thought it's better to just use `drop`.<br> </div> Sat, 05 Oct 2024 14:37:24 +0000 Abuses of mutable references https://lwn.net/Articles/992655/ https://lwn.net/Articles/992655/ farnz <p>Note when doing that sort of analysis that you also have to take into account <tt>std::mem::swap</tt> and friends. An exclusive reference to a type I cannot construct works fine for what you describe, but once you allow a user-chosen type into the closure parameters, you have to consider what happens if I swap it out for another one I have lying around. I <em>think</em> that for the case of a single layer of locking, it's fine, but it becomes more exciting once you consider the case of nested locks, and my brain isn't up to looking for obscure corner cases in that situation. Wed, 02 Oct 2024 13:44:31 +0000 Locking https://lwn.net/Articles/992619/ https://lwn.net/Articles/992619/ excors <div class="FormattedComment"> I think that if you have a closure, there's no need for anything that tricky - you can just pass an exclusive reference to some object into the closure. The closure can't drop the object or leak the reference outside of its scope. The closure can't fail to return except by infinitely looping or blocking (which is relatively easy to debug by looking at the offending thread's call stack). When the closure returns, you know you've got the object back, and you can do any relevant cleanup.<br> <p> For example, mutexes could be designed like:<br> <p> let mut m = ScopedMutex::new(42);<br> m.with_lock(|i: &amp;mut i32| { println!("{}", i); });<br> <p> where with_lock guarantees it will always lock and unlock correctly around the accesses (unless the closure never returns).<br> <p> The issue is, it would be nice to get similar guarantees *without* the closure, because closures create a potentially-inconvenient nested code structure and can introduce some extra lifetime challenges. Usually that's fine, but sometimes it's rather annoying. Better to have an API like:<br> <p> let mut m = Mutex::new(42);<br> let i: MutexGuard&lt;_&gt; = m.lock().unwrap();<br> println!("{}", i);<br> std::mem::forget(i); // this should be an error - it should be required to properly drop i (by letting it go out of scope, or calling i.unlock() which transfers ownership of i, etc)<br> <p> Currently the forget() is allowed and the mutex will never get unlocked, and some unrelated code may deadlock later (which is much harder to debug). It also means the Mutex might be dropped while it is still locked, so Mutex has to know how to clean up from that unusual state. Prohibiting the forget() (and any other code with similarly forgetful behaviour) will require new Rust language features.<br> <p> (I think that's an easy trap to fall into when designing APIs: you design a MutexGuard whose lifetime is less than Mutex's lifetime, so you know the MutexGuard cannot be dropped after the Mutex is dropped (which is true, and a very helpful guarantee), and intuitively that means the MutexGuard will be dropped before the Mutex is dropped. And that's almost always true, but it's not guaranteed - the MutexGuard might have been forgotten instead of dropped - so you have to either handle that correctly (which can be difficult), or switch to a closure-based API.)<br> <p> </div> Wed, 02 Oct 2024 12:56:58 +0000 Locking https://lwn.net/Articles/992610/ https://lwn.net/Articles/992610/ taladar <div class="FormattedComment"> Couldn't you just force (assuming code doesn't panic or otherwise never return) the "give it back" semantics by making the type of the closure return the opaque type you pass in that the closure has no way to construct and so it is forced to return the one it got in its own parameter?<br> </div> Wed, 02 Oct 2024 10:45:19 +0000 Locking https://lwn.net/Articles/992119/ https://lwn.net/Articles/992119/ Cyberax <div class="FormattedComment"> <span class="QuotedText">&gt; 2) experimental and not well-understood</span><br> <p> Deferred cleanup has been used in C++ and Go for _decades_ and is very well understood. The main rule is: do NOT mix it with goto-based control. It can even be automated via a simple checkpatch.pl rule.<br> <p> In that case, the of_node_put should also be rewritten into the guard-style cleanup.<br> </div> Sun, 29 Sep 2024 07:22:49 +0000 Locking https://lwn.net/Articles/992115/ https://lwn.net/Articles/992115/ viro <div class="FormattedComment"> <span class="QuotedText">&gt; The C cleanup-based guards were recently added, in 2023. So the decision was made, by Linus and others, that the advantages outweigh your concerns. I don't see why this needs to be relitigated for Rust.</span><br> <p> A nice bit of MBAese, that... Opposition between "advantages" on one hand and "your concerns" on another is particularly charming - the former being implicitly objective and unchanging, while the latter - subjective and theoretical in the best case. Use of passive-with-reference-to-management syntax is also impressive. Well done.<br> <p> Back in the real world, cleanup.h contains tools that are<br> 1) potentially useful<br> 2) experimental and not well-understood<br> 3) demonstrably dangerous in incautious use - and that's demonstrably, not theoretically. Fresh example: <a href="https://lore.kernel.org/lkml/20240927-reset-guard-v1-1-293bf1302210@pengutronix.de/T/">https://lore.kernel.org/lkml/20240927-reset-guard-v1-1-29...</a><br> <p> The tools in question have nothing to do with Rust, so their relevance in this thread is, indeed, questionable. Who'd dragged them in, I wonder... &lt;checks&gt; <a href="https://lwn.net/Articles/991431/">https://lwn.net/Articles/991431/</a> is where that had happened, so it would be yourself, wouldn't it?<br> </div> Sat, 28 Sep 2024 23:12:31 +0000 Localised bugs https://lwn.net/Articles/991907/ https://lwn.net/Articles/991907/ james For what it's worth, I think gmatht was referring to the British sit-com Red Dwarf, where they explored this management failing in depth. Thu, 26 Sep 2024 16:22:54 +0000 Localised bugs https://lwn.net/Articles/991898/ https://lwn.net/Articles/991898/ pizza <div class="FormattedComment"> <span class="QuotedText">&gt; Doesn't that describe the modern car, though? :-)</span><br> <p> As someone whose $dayjob currently revolves around SoCs targeting next-gen Automobiles, yes and no.<br> <p> Yes in the sense that these systems _may_ share common physical communication buses, but No in the sense that when they do, there are figurative (as well as literal) firewalls designed into the overall system to ensure only suitably blessed messages are acted upon by any given component.<br> <p> That isn't to say that bugs can't occur [1], just that this class of bug is nearly always due to incorrect/incomplete specifications, typically due to poorly-thought-out scope creep [2], not traits of the language used to implement the specification.<br> <p> [1] I recall reading that someone was able to trigger brake lockup on some Jeep models via their cellular modems<br> [2] eg by exposing what was once a completely private and trusted bus to the open internet with no authentication to enable remote start capabilities.<br> </div> Thu, 26 Sep 2024 15:11:56 +0000 Localised bugs https://lwn.net/Articles/991896/ https://lwn.net/Articles/991896/ Wol <div class="FormattedComment"> <span class="QuotedText">&gt; If your reactor core is capable of venting radiation into crew quarters and/or your soup dispenser is connected to the reactor in any way, you have far more serious problems than the language the language used to write their respective control software.</span><br> <p> Doesn't that describe the modern car, though? :-)<br> <p> Cheers,<br> Wol<br> </div> Thu, 26 Sep 2024 14:15:58 +0000 Localised bugs https://lwn.net/Articles/991831/ https://lwn.net/Articles/991831/ pizza <div class="FormattedComment"> <span class="QuotedText">&gt; but an off-by-one error shouldn't cause the reactor to vent radiation into the crew quarters. </span><br> <p> If your reactor core is capable of venting radiation into crew quarters and/or your soup dispenser is connected to the reactor in any way, you have far more serious problems than the language the language used to write their respective control software.<br> <p> <p> <p> </div> Thu, 26 Sep 2024 13:35:52 +0000 Localised bugs https://lwn.net/Articles/991817/ https://lwn.net/Articles/991817/ gmatht <div class="FormattedComment"> As I understand, bugs in safe rust are likely only to affect the module in which the bugs occur (and which call the affected module, etc). For example, if you had an intern who was a bit of a space cadet, you could get them to clean up the gazpacho soup module. Sure, the soup dispenser might start endlessly dispensing "trout a la creme", but an off-by-one error shouldn't cause the reactor to vent radiation into the crew quarters. <br> </div> Thu, 26 Sep 2024 10:00:16 +0000 Locking https://lwn.net/Articles/991727/ https://lwn.net/Articles/991727/ NYKevin <div class="FormattedComment"> One can argue that drop(guard) is just a funny way of spelling (the currently nonexistent) guard.unlock().<br> </div> Wed, 25 Sep 2024 17:54:19 +0000 Locking https://lwn.net/Articles/991587/ https://lwn.net/Articles/991587/ NYKevin <div class="FormattedComment"> So I looked around a bit more and there are at least two crates which already do something resembling the above:<br> <p> <a href="https://docs.rs/ordered-locks/latest/ordered_locks/">https://docs.rs/ordered-locks/latest/ordered_locks/</a><br> <a href="https://docs.rs/lock_ordering/latest/lock_ordering/">https://docs.rs/lock_ordering/latest/lock_ordering/</a><br> <p> So yes, this is a thing you can do. Whether it is entirely feasible for very large and complicated lock hierarchies is still unclear to me.<br> </div> Tue, 24 Sep 2024 22:34:42 +0000 QOTW https://lwn.net/Articles/991585/ https://lwn.net/Articles/991585/ tialaramex <div class="FormattedComment"> I actually ended up learning a lot more C++ as a result of learning Rust. But as a result I like it even less now than I did five years ago when I had written no Rust. I do not think I would recommend learning C++, and I particularly cannot recommend the choice (apparently made by several institutions) to teach C++ as first language.<br> <p> <p> <p> </div> Tue, 24 Sep 2024 22:34:13 +0000 Locking https://lwn.net/Articles/991549/ https://lwn.net/Articles/991549/ NYKevin <div class="FormattedComment"> In general, sequencing of operations can be enforced using typestate APIs. The broad idea is that the earlier operation returns a thing (which can be zero-size, i.e. nonexistent at runtime) which is consumed (or borrowed, depending on the desired semantics) by the next operation in the sequence. Technically, lock guards are an example of this pattern, since you cannot access the underlying data without going through the Mutex, and you cannot unlock the Mutex without dropping the guard. So it is impossible to access data without locking the Mutex.<br> <p> The more difficult problem is preventing deadlock (which has never been an explicit goal of Rust's safety guarantees). Here's a simple way to at least prevent a lock from being recursively acquired:<br> <p> * Invent a move-only wrapper (i.e. a type that is not Copy or Clone) for &amp;Mutex. Each thread has this wrapper instead of the actual &amp;Mutex, and the &amp;Mutex is private so you can't directly get it out again.<br> * The wrapper has methods to lock the underlying Mutex as usual, but those methods borrow the wrapper mutably to ensure they cannot be called more than once.<br> * The wrapper is also !Send (otherwise, one thread could end up owning two of them and break the rules).<br> <p> But this is unideal for at least two reasons:<br> <p> 1. The whole point of Mutexes is to allow shared access to mutable data. A move-only wrapper is just undoing all of that hard work. Yes, it's per-thread, but that's still painful.<br> 2. It does not solve the broader lock hierarchy problem.<br> <p> So I thought some more, and came up with a more elaborate idea, which works as follows:<br> <p> * Each distinct lock in the lock hierarchy gets its own &amp;Mutex wrapper (but those wrappers are Clone and Copy, so you can do all the same things you can do with &amp;Mutex now). You can maybe use macros to make the wrappers less tedious to write.<br> * Each wrapper impls a generic trait that indicates its relationship with other locks in the hierarchy. For example, impl LockAfter&lt;A&gt; for B{} would mean "you may lock B after you have locked A."<br> * There's an empty struct (actually it has a PhantomData, but it's "empty" in the sense that it takes up zero bytes and gets constant-folded out of existence) which can only be constructed by these wrappers, or by some private initialization function that is callable from whatever "management" or runtime code is directly responsible for spawning threads. The struct has a generic type parameter which can either be one of the wrapper types, or it can be (). It also has a lifetime parameter, which it may not outlive (as constrained with PhantomData) To avoid having to write "the struct" over and over again, let's just say this struct is called LockHierarchy&lt;'a, T&gt;.<br> * LockHierarchy is not Clone. You have to pass it by value everywhere (but at runtime, it vanishes, so you can pass it around as much as you want without incurring any overhead).<br> * LockHierarchy is also !Send, so that no thread can ever own more than one.<br> * When a thread is created, the caller gives it a LockHierarchy&lt;'static, ()&gt; instance (created using the private initialization function). This construction must be done in the callee thread's startup code and not on the main thread, because it is !Send and can't be transferred, but this is not really much of a problem since the thread-spawning code can simply arrange for the thread to construct this object before it calls into the application code.<br> * Each wrapper takes a &amp;'b mut LockHierarchy&lt;'a, T&gt; argument to lock(), and returns both a lock guard and a new LockHierarchy&lt;'b, U&gt; instance, where U is the type of the wrapper, T is trait-constrained to come before U in the lock hierarchy (or T = () is allowed unconditionally), 'b is the lifetime parameter of the lock guard, and 'a must outlive 'b. The latter constraint does not need to be spelled out, since it is implied by the validity of the type &amp;'b mut LockHierarchy&lt;'a, T&gt;.<br> * At any given time, each thread should have exactly one LockHierarchy object (which is not borrowed mutably), that object is required in order to take any locks, and the type of the LockHierarchy object determines which locks you are still allowed to acquire. Each LockHierarchy's lifetime parameter stops it from outliving its associated lock guard.<br> * A thread can e.g. std::mem::forget() the LockHierarchy, or drop it, but oh well, that just means you can't lock anything. It doesn't cause unsafety or other problems, because LockHierarchy is just a trivial opaque object with no internal machinery.<br> <p> I *think* this comprehensively prohibits any lock inversions from happening, statically, with zero overhead, provided that all mutexes in the system participate and your hierarchy really is acyclic. I also think you can probably use dyn as an "escape hatch" to check lock ordering at runtime instead of compile time, if it turns out that it's too hard to prove that some code is sound.<br> <p> But I have not actually tried to implement this, so there might be some problem with it that I cannot see. For example, I'm not sure that Rust's trait solver is smart enough to materialize the whole DAG of an arbitrary lock hierarchy, so you might need to use macros for the hierarchy traits as well as the wrappers. Or you might tell everyone to manually write out the individual hierarchy traits they need, and check for cycles with some sort of linter or static analysis tool.<br> </div> Tue, 24 Sep 2024 22:03:39 +0000 Locking https://lwn.net/Articles/991576/ https://lwn.net/Articles/991576/ Cyberax <div class="FormattedComment"> <span class="QuotedText">&gt; ... except that when you have several levels of nesting, finding the end of the right scope 80 lines down from where you've taken the sucker can be an interesting exercise. spin_unlock(...) is a lot more recognizable than one } in the series of 4.</span><br> <p> Here's a thought: why does that matter? Is that because the scope is too large? Then it's probably a good idea to extract the locked code into its own scope. In my code, I also try to take the lock at the beginning of a scope, so the entire scope itself becomes a visual indicator of what's locked. The kernel code style with 8-space tabs makes this less practical in C, but Rust is different.<br> <p> And if you want to re-lock the object, you need to look up for the locks that were just taken, instead of down to see the unlocks. It's a bit different perspective, but it's about the same level of mental overhead.<br> <p> <span class="QuotedText">&gt; What's less obvious is that it applies to goto around the guard - there's no visible spin_unlock(...) in the end, but it is implicit and there's exact same kind of bug. </span><br> <p> Yes, the two styles are completely incompatible, and it's better not to mix them at all in one function (perhaps, a checkpatch.pl rule?). Good news is that Rust will not allow this kind of error.<br> </div> Tue, 24 Sep 2024 21:04:04 +0000 Locking https://lwn.net/Articles/991574/ https://lwn.net/Articles/991574/ roc <div class="FormattedComment"> The C cleanup-based guards were recently added, in 2023. So the decision was made, by Linus and others, that the advantages outweigh your concerns. I don't see why this needs to be relitigated for Rust.<br> </div> Tue, 24 Sep 2024 20:53:06 +0000 Locking https://lwn.net/Articles/991571/ https://lwn.net/Articles/991571/ viro <div class="FormattedComment"> ... except that when you have several levels of nesting, finding the end of the right scope 80 lines down from where you've taken the sucker can be an interesting exercise. spin_unlock(...) is a lot more recognizable than one } in the series of 4.<br> <p> BTW, do *NOT* mix that with goto-based cleanups without a lot of care. Any goto *into* that scope must come from that scope itself, which would be trivial if not for the fact that guard() can be not the first thing within the compound statement. In that case the scope extends from guard() to the end of compound statement. Now, it's very obvious that<br> <p> if (condition) goto exit_something;<br> {<br> some_type var = foo();<br> ....<br> exit_something:<br> something(var);<br> ...<br> }<br> is a bug - no matter how liberal your interpretation of standard might be, on the path that takes this goto exit_something you have var used uninitialized. Anyone tempted to add such goto would see that this candidate failure exit is not suitable, no matter how similar it might be to what you want. And compiler will catch that if you try.<br> <p> What's less obvious is that it applies to goto around the guard - there's no visible spin_unlock(...) in the end, but it is implicit and there's exact same kind of bug. Worse, gcc 12 and earlier does not even warn you - it goes ahead and produces broken code. clang does catch it properly, but neither gcc nor eyeball do.<br> <p> So _if_ you use __cleanup-based cleanups, be very careful about labels in the scope - any goto from outside of scope to that will be trouble. With zero visible indication that such and such failure exit is *NOT* suitable to jump into from before the declaration that carries __cleanup on it. Gets especially nasty when the damn thing sits in the middle of function body, not nested at all. Do that and you've turned the goto-based cleanups you might have there into bugs.<br> <p> It's not a theoretical concern - I've run into just that several time this cycle. Ended up with doing cross-builds on clang (and excluding the targets not supported by clang - thankfully, build coverage had not suffered in the places I needed to touch), but step into that while debugging something and forget about gcc missing such stuff and you are looking into a really fun debugging session...<br> <p> In some cases it's a useful tool, but it's _really_ not something that could be used without (apriori non-obvious) care.<br> </div> Tue, 24 Sep 2024 20:49:53 +0000 Forcing explicit destructuring https://lwn.net/Articles/991572/ https://lwn.net/Articles/991572/ intelfx <div class="FormattedComment"> <span class="QuotedText">&gt; There is some thought being put into whether something like undroppable types would be a useful addition to the language</span><br> <p> So… true linear types (as discussed right in this comment section)? :-)<br> </div> Tue, 24 Sep 2024 20:21:56 +0000 Forcing explicit destructuring https://lwn.net/Articles/991569/ https://lwn.net/Articles/991569/ farnz <p>There is some thought being put into whether something like <a href="https://smallcultfollowing.com/babysteps/blog/2023/03/16/must-move-types/">undroppable types</a> would be a useful addition to the language. These would allow Rust for Linux to have a scope guard that must be explicitly destroyed (probably via a <tt>fn unlock(self)</tt> function), and where dropping them without calling that function is a compile-time error. Tue, 24 Sep 2024 20:19:15 +0000 Locking https://lwn.net/Articles/991563/ https://lwn.net/Articles/991563/ daroc <p> One also can drop the guard explicitly: </p> <pre> drop(guard); </pre> <p> So I don't think it's less flexible, really. But, as in every discussion about programming languages, it's not really about what's <em>possible</em> so much as what the language makes easy or hard. I think it's a good point that Rust's locking is less explicit! That's certainly true, and it is a tradeoff. I (personally) think that the guarantees the compiler provides are worth it, but that doesn't mean we shouldn't acknowledge that less explicit locking does have downsides. </p> Tue, 24 Sep 2024 20:02:21 +0000 Locking https://lwn.net/Articles/991560/ https://lwn.net/Articles/991560/ Cyberax <div class="FormattedComment"> <span class="QuotedText">&gt; ... and it's a trouble waiting to happen. Look around and you'll find all kinds of bad ideas, most with exact same reason - hard to reason about the program state. </span><br> <p> Why?<br> <p> The reasoning here is simple: everything is locked after the scope guard is taken until the end of the scope. And you can't access the protected data accidentally without taking a lock. You also can't forget to clean up the lock in "goto cleanup". Early returns are also not a problem anymore.<br> <p> It does reduce the flexibility a bit, but hand-over-hand locking is pretty rare.<br> </div> Tue, 24 Sep 2024 19:44:06 +0000 Locking https://lwn.net/Articles/991544/ https://lwn.net/Articles/991544/ daroc <p> The parent-before-child order is actually fairly easy to enforce in Rust without using the technique from the talk (which was focused on imposing a global order). For the tree case, imagine a node looks like this: </p> <pre> struct Node { some_unprotected_data: usize, protected_data: Mutex&lt;NodeInner&gt;, } struct NodeInner { more_protected_data: usize, children: Vec&lt;Node&gt;, } </pre> <p> By putting all the data that should only be accessed with a Mutex held inside an inner structure like that, we can enforce that the program can only have references to it that live as long as the lock is held. So if I had a reference to a Node, I couldn't access the children until I locked the Mutex. And then I could only hold on for references to <em>them</em> until I unlocked the Mutex, at which point the compiler would no longer let me use them. </p> Tue, 24 Sep 2024 17:31:52 +0000 Locking https://lwn.net/Articles/991543/ https://lwn.net/Articles/991543/ viro <div class="FormattedComment"> Details would be very interesting... Basically, what kind of information do you need to put into declarations and how expressive the mechanism is - can it e.g. deal with "you need to take locks on the tree nodes in the parent-before-child order" kind of rules? That's where the things tend to get tricky...<br> </div> Tue, 24 Sep 2024 17:21:50 +0000 Locking https://lwn.net/Articles/991435/ https://lwn.net/Articles/991435/ viro <div class="FormattedComment"> ... and it's a trouble waiting to happen. Look around and you'll find all kinds of bad ideas, most with exact same reason - hard to reason about the program state. With varying unpleasantness - a few goto in a 10-line function could be just fine, but it can easily get much worse.<br> <p> As those things go, guard() is pretty high on the "easily turned into a landmine" scale. scoped_guard() is saner, but it comes with its own set of headache sources - deeply nested structure can get confusing.<br> <p> It's not about preserving some kind of purity; there's no such thing in the real world anyway. Bitrot _is_ a fact of life; there's no One True Tool/Style/Language that would prevent it. What matters is how brittle the thing is. Another fact of life is that trying to figure out a bug elsewhere will lead you through a lot of unfamiliar code (possibly - yours, but with detailed memories of the area swapped out of active memory; when there's a dozen of areas you need to get through, the latency of mental context switch can be very painful); you will need to make decisions about the next thing to look into, and do that based on the reasoning about unfamiliar code. It _can't_ be avoided; what matters is how hard will it be. And yes, that includes the need to modify something you've written ten years ago. It happens. And it's all about tradeoffs.<br> <p> As for the __cleanup-based tools... in moderation it's fine, but blind use can lead to a lot of PITA. In particular, it comes with serious, er, adverse interactions with other tools. We'll see how bad a source of bitrot it will be; for now I'm very cautious about the cases where accidentally delaying the cleanup can cause problems, and locking is firmly in that class.<br> </div> Tue, 24 Sep 2024 17:09:12 +0000 > Guo joked that the best way to learn Rust was to learn C++, hate it, and then learn Rust. https://lwn.net/Articles/991530/ https://lwn.net/Articles/991530/ aragilar <div class="FormattedComment"> <a href="https://docs.rs/bstr/latest/bstr/">https://docs.rs/bstr/latest/bstr/</a> also makes this easier when you know you're dealing with probably-UTF-8.<br> </div> Tue, 24 Sep 2024 14:48:57 +0000 Locking https://lwn.net/Articles/991522/ https://lwn.net/Articles/991522/ ehiggs <div class="FormattedComment"> Note: all my experience is in user space, using Rust with access to the std library.<br> <p> <span class="QuotedText">&gt; How would the compiler verify that a call you've added in the area under mutex will not lead to the same mutex being grabbed?</span><br> <p> In some cases, tools can find this. For example in this Playground link I lock a mutex twice. The compiler will build and run the executable but deadlocks. If we use Tools-&gt;Miri, the issue is found. But I think this is found by running the program in an interpreter rather than detecting it at compile time.<br> <p> <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=98af4e48d4b202cbb21ecb24137c5b21">https://play.rust-lang.org/?version=stable&amp;mode=debug...</a><br> <p> For more elaborate issues, the language will make the types messier and messier to work with as you venture further out of bounds.<br> <p> For example if you have a `my_lock = std::sync::Mutex&lt;MyData&gt;` then getting access to `MyData` involves calling `my_lock.lock()` which gives a `MutexGuard&lt;MyData&gt;`. You can't pass a `MutexGuard&lt;MyData&gt;` to a function expecting `&amp;MyData` so you need to try to get the reference to `&amp;*MyData` - a reference to a dereference. Already, this is looking a bit weird and hopefully helps a reviewer. But you also have the MyData reference and pass that around. <br> <p> While I C you might think 'how can I get access to the pointer and make sure it's locked', in the Rust case if you can find access to the reference then you know it's locked - otherwise it would have still been behind the Mutex wrapper.<br> </div> Tue, 24 Sep 2024 14:01:40 +0000 Locking https://lwn.net/Articles/991493/ https://lwn.net/Articles/991493/ daroc <div class="FormattedComment"> The Rust standard library mutexes won't protect you from this, and neither will the ones designed (so far) in the kernel. But after Kangrejos, I went to RustConf. One of the presentations there was about how to encode lock dependency ordering into the type system so that it can be checked at compile time. So ensuring that there are no deadlocks from cyclic mutexes is possible, and the Rust-for-Linux folks could adopt that approach if they wanted to.<br> <p> Keep an eye out for an article about that in the coming weeks.<br> </div> Tue, 24 Sep 2024 11:55:05 +0000 New Tools https://lwn.net/Articles/991455/ https://lwn.net/Articles/991455/ Wol <div class="FormattedComment"> The computer is an idiot, it knows not how and when,<br> In fact the only thing it knows is 1+1 is 10<br> <p> It's not that Rust programs don't contain bugs. It's that a Rust program is much more likely to do what you (thought you) told it to. The Rust compiler is very aggressive about coming back and saying "I don't understand", whereas the C compiler will just go off and do its best.<br> <p> Doesn't stop Rust being full of "Do What I Mean Not What I Say" bugs ...<br> <p> Cheers,<br> Wol<br> </div> Tue, 24 Sep 2024 10:14:14 +0000 New Tools https://lwn.net/Articles/991453/ https://lwn.net/Articles/991453/ LtWorf <div class="FormattedComment"> <span class="QuotedText">&gt; perhaps we can just never compile a buggy driver. </span><br> <p> I'm not comfortable with the mental model that rust programs have no bugs to be honest.<br> </div> Tue, 24 Sep 2024 09:43:34 +0000 New Tools https://lwn.net/Articles/991434/ https://lwn.net/Articles/991434/ Wol <div class="FormattedComment"> <span class="QuotedText">&gt; Trying to apply familiar idioms from C can lead to significant challenges and frustration when working with Rust.</span><br> <p> But isn't that true for ANY change in language?<br> <p> I program C like it's Fortran. Using DataBASIC requires a completely different way of thinking. Scheme, well... and of course there's Forth ... :-)<br> <p> Even comparing Scots and English :-)<br> <p> Cheers,<br> Wol<br> </div> Tue, 24 Sep 2024 07:17:20 +0000 Locking https://lwn.net/Articles/991431/ https://lwn.net/Articles/991431/ roc <div class="FormattedComment"> <span class="QuotedText">&gt; How would the compiler verify that a call you've added in the area under mutex will not lead to the same mutex being grabbed? If the answer is "it won't", you need to be able to see that without its assistance. The lack of explicit unlock makes that much harder to see.</span><br> <p> As noted in this article, the kernel is already using scoped guards in C. E.g.:<br> <a href="https://github.com/torvalds/linux/blob/abf2050f51fdca0fd146388f83cddd95a57a008d/sound/core/seq/seq_fifo.c#L201">https://github.com/torvalds/linux/blob/abf2050f51fdca0fd1...</a><br> so it's too late to require explicit "unlock".<br> </div> Tue, 24 Sep 2024 06:58:50 +0000 Locking https://lwn.net/Articles/991430/ https://lwn.net/Articles/991430/ vasvir <div class="FormattedComment"> Thanks - you just saved me a trip to this rabbit hole and back...<br> </div> Tue, 24 Sep 2024 06:53:34 +0000 > Guo joked that the best way to learn Rust was to learn C++, hate it, and then learn Rust. https://lwn.net/Articles/991422/ https://lwn.net/Articles/991422/ hunger <div class="FormattedComment"> That's standard fare for decades now in a ton of programming languages, not just Rust. I find it depressing how disconnected from general language developments Linux Kernel devs seem to be.<br> <p> I just hope rust won't suffer with useless features, added only to please some old guys in the kernel community.<br> </div> Tue, 24 Sep 2024 06:15:06 +0000 Discussion results https://lwn.net/Articles/991425/ https://lwn.net/Articles/991425/ dirklwn <div class="FormattedComment"> There are some results from the discussion, already. A "kernel-docs: Add new section for Rust learning materials" patch [1] adding some of the collected resources to the kernel's Documentation has been posted. And in the Rust for Linux Zulip chat some proposals are shared how a "Linux kernel – From C to Rust for Linux" documentation [2] could look like.<br> <p> [1] <a href="https://lore.kernel.org/rust-for-linux/20240922160411.274949-1-carlos.bilbao.osdev@gmail.com/">https://lore.kernel.org/rust-for-linux/20240922160411.274...</a><br> [2] https://rust-for-linux.zulipchat.com/#narrow/stream/288089-General/topic/Documentation.3A.20From.20kernel's.20C.20to.20Rust.20For.20Linux/near/469300705<br> </div> Tue, 24 Sep 2024 05:59:34 +0000 New Tools https://lwn.net/Articles/991423/ https://lwn.net/Articles/991423/ dirklwn <div class="FormattedComment"> <span class="QuotedText">&gt; Rust requires adopting a different mental model for programming</span><br> <p> Yes, exactly!<br> <p> And the question we should discuss about how to make it easier for people with the C-kernel mental model to adopt.<br> </div> Tue, 24 Sep 2024 05:40:48 +0000 Locking https://lwn.net/Articles/991416/ https://lwn.net/Articles/991416/ viro <div class="FormattedComment"> How would the compiler verify that a call you've added in the area under mutex will not lead to the same mutex being grabbed? If the answer is "it won't", you need to be able to see that without its assistance. The lack of explicit unlock makes that much harder to see.<br> <p> Sure, you don't lock to protect the code, or even the data - you lock to protect the invariants. But code is what you change, so unless one's answer to everything (including debugging) is "throw it all away and rewrite from scratch", you do need to be able to reason about the locking state at given spot in the code...<br> </div> Tue, 24 Sep 2024 02:56:47 +0000 Locking https://lwn.net/Articles/991407/ https://lwn.net/Articles/991407/ aszs <div class="FormattedComment"> Not mathematician but went down a googling rabbit hole.<br> <p> The terms "linear types" and "affine types" comes from substructural logic theory which in turn borrows those terms from linear and affine equations in pure math theory. <br> <p> The connection is this:<br> <p> linear equations are of the form 𝑓(𝑥)=𝑎𝑥 and affine are 𝑓(𝑥)=𝑎𝑥+𝑏, where 𝑎 and 𝑏 are arbitrary constants.<br> So in the logic: <br> <p> Linear: f(x) = a*x, (x^1 as opposed to x^2...) "use x once" <br> Affine: f(x) = a*x + b = a*x^1 + b*x^0, "use x once or zero times".<br> <p> perfectly obvious!<br> </div> Mon, 23 Sep 2024 20:45:28 +0000 Locking https://lwn.net/Articles/991403/ https://lwn.net/Articles/991403/ NYKevin <div class="FormattedComment"> <span class="QuotedText">&gt; There's just a whole lot of things that could be a lot nicer with some sort of "here's a thing, please give it back to me when you're done with it" mechanism.</span><br> <p> You can steal the API from std::thread::scope(), but I'm not sure how applicable that is in all cases. The basic idea is as follows:<br> <p> * The caller passes in a closure, which takes some opaque scope object by reference.<br> * The scope object provides methods for the closure to do whatever thing(s) the caller wants to do (in the case of thread::scope(), spawn threads).<br> * The scope object internally keeps track of whatever thing(s) the closure does, and whatever cleanup operation(s) may be required in response (in the case of thread::scope(), join those threads to ensure they do not outlive the scope).<br> * Because the closure is called from the callee, the callee always gets control back after the closure returns (or panics, if unwinding is enabled and we use catch_unwind). The callee can ensure that all cleanup code is executed, by referencing the scope object's internal data. Since the closure does not receive ownership of the scope object, it cannot forget it.<br> * In the case of thread::scope(), the scope object also gives the closure "handles" which it can use to reference the operations (threads) it has performed (spawned). But if the closure forgets these handles, it has no effect on the scope object, because they're just handles - they do not need to be dropped for the scope object to do the appropriate cleanup.<br> * Lifetime parameters are used to prohibit the closure from smuggling these objects and references out to the caller and trying to carry out further mischief with them.<br> <p> This is obviously not as flexible as true linear types would be, but it is better than nothing.<br> </div> Mon, 23 Sep 2024 19:51:41 +0000