Does this mean that all of the standard library is written using manual memory management? Wouldn't this slow down its development and expose it to all the same reliability and security problems C has?
This isn't correct, it relies heavily on GC.
It's worth remembering that Rust has a form of GC as well - it uses a reference counting collection mechanism which is executed at scope boundaries instead of as a separate thread or co-routine. This doesn't prevent writing Kernels or userspace applications in Rust.
You don't want a garbage collector in a kernel, because there's a lot of code that needs to run in soft realtime - aside from timekeeping, there's lots of hardware that'll enter a failure mode or drop data if you don't respond to it in time.
Eep.
> there's a lot of code that needs to run in soft realtime
This is not really a problem, since the hardware will trigger an interrupt which will, at the hardware level, stop the current instruction and move the instruction pointer to a registered location in code. Most time-sensitive hardware interaction occurs in these interrupt handlers, the kernel simply has to read/populate buffers with data which the interrupt handlers consume/fill.
Since interrupts happens to existing kernels all the time (as well as user-space GC runs as well), and so long as the interrupt handler can properly run without having to mess with memory being managed by the GC (which might, to be fair, require a bit of lower level code), you could resume back into a GC cycle as easily as you can resume into a normal kernel.
At which point the interrupt handler needs to route the interrupt through a complex, multi-layer driver stack, and the driver needs to respond - where it's likely to depend on complex datastructures of the type that would normally be managed by a GC if the response is anything but an absolutely trivial ack.
All garbage collectors require that at least some memory is inaccessible to regular code while the garbage collector is running. As a result, you essentially have to ensure that anything that might ever be touched as a result of an interrupt, or during another soft-realtime event, is not managed by the garbage collector - and then you may have to detangle even more data from the GC's grasp when the hardware vendor releases the next revision of their hardware which requires access to more information. You also have no proof that what you're doing outside the safety of the GC is correct without a Rust-like lifetime system, or other more complex static verification systems (such as have been built on top of Coq).
In addition to that, developing performant code with few/no obvious hangs under a GC takes a lot of effort and specifically fighting against the GC - ask any game developer who's developed a large game using C# under Unity or the like - and they get the luxury of getting to run GC at vsync. You don't have any good spot to run GC in a kernel.
And it's opt-in: you only pay for reference counting if you explicitly wrap your type in Rc. Moreover (and in contrast to Nim), you don't need to use reference counting for memory safety.
If you limit yourself to boxed and stack variables, which is a pretty big limit when writing larger programs.
And as noted by a sibling comment, you can create a memory leak due to cycles if you use it imprudently - do we consider that to be a safe use of memory?
That's more or less how most C or C++ programs work, however. C and C++ programs (including the Linux kernel) use reference counting when the object lifetime is truly dynamic. For example, file objects going back to the very first Unix have had to be reference counted, because they can be duplicated via the dup() system call, inherited via fork(), or sent from process to process via cmsg.
The big advantage of the C/C++/Rust approach comes from the observations that (a) the vast majority of objects only have one owner; (b) reference counting does not have a global performance impact when used only for a small subset of objects.
> And as noted by a sibling comment, you can create a memory leak due to cycles if you use it imprudently - do we consider that to be a safe use of memory?
It's a tradeoff: reference counting is bad at cycles, but has advantages due to prompt deallocation, avoidance of the mark phase, and (in Rust) avoids unnecessary cross-thread synchronization. I don't think it's unsafe, because leaks can happen in any language, including garbage-collected ones: you can have arrays in global variables that grow without limit, for example.
Remember that the criterion that GC uses—no more references to an object—is ultimately a heuristic approximating what you really want, which is whether the program will access the data in question again. Of course, due to the halting problem, we can't actually solve that problem, so all garbage collection algorithms approximate it, with the requisite possibility of leaks.
i.e. these "specific requirements and limitations" you mention have a LOT to do with memory.
Just because there's a GC on the kernel's memory doesn't mean that the GC is somehow now working on all the process' memory that the kernel is running. That's not how the kernel works. The page table just holds a bunch of information about which processes are using which pages, garbage collecting a particular page table entry because the entry wasn't being used anymore wouldn't cause the memory which the entry was pointing to to suddenly be destroyed. And if the page table entry somehow gets to the point where it could be garbage collected that would mean that nothing is referencing it anymore, which would (in a properly written kernel free of bugs!) mean that the process isn't using the memory anymore, so there's no harm done.
EDIT: Real life example of a GC'ed kernel: