5 ms·
OOM is an abort, not a panic (contrary to the official docs, interestingly) > you're free to provide an alternative stdlib But like... should you have to? >
by acconsta 11y ago
OOM is an abort, not a panic (contrary to the official docs, interestingly)
> you're free to provide an alternative stdlib
But like... should you have to?
> for those rare occasions
In kernel programming, allocation failures are common and handling them is essential. To quote my (very talented) classmate, who actually wrote part of a kernel in Rust:
"The only really major issue is with how allocation failure works, which makes rust a (very) poor choice for real kernel development"
https://www.reddit.com/r/rust/comments/341v3n/cs_honors_thesis_reenix_implementing_a_unixlike/ https://www.reddit.com/r/rust/comments/341v3n/cs_honors_thes...
- dbaupp 11y ago> But like... should you have to? This is the status quo (in all languages) if you're doing embedded work: you lose most of the standard library. Rust goes slightly beyond that default by bundling `core`, which is designed for these environments and serves as a building block on which embedded/bare-metal development can flourish. (Cargo works fine with such things, so one can even distribute alternate standard libraries on crates.io.) > "The only really major issue is with how allocation failure works, which makes rust a (very) poor choice for real kernel development" I think this was the result of bravely trying very hard to shoe-horn `std` and the inflexible `box` syntax into a kernel environment, somewhere both are 100% not designed for. The recommended approach is to just use `core` and layer non-`std` libs on top of that.
- acconsta 11y ago1. In practice most embedded toolchains will give you some of the standard library. A standards compliant C++ freestanding library provides new and delete, for example. 2. The libcore allocator API is marked unstable, so even if you go through the trouble of implementing it, how long will it last?
- Manishearth 11y ago> will give you some of the standard library. That's what libcore is. A lot of the stdlib is reexports over libcore. new and delete are C++ isms, their Rust counterparts are `Box::new` (and destructors are automatic). `Box::new`, like `new`, has the OOM issue. If you're okay with that, you are free to link to the `alloc` crate, which gives you `Box` without pulling in additional deps. There are plans for in-place boxing, and AFAICT that API will be available to embedded users.
- dbaupp 11y ago> There are plans for in-place boxing, and AFAICT that API will be available to embedded users. Already exists, unstably: http://doc.rust-lang.org/nightly/core/ops/trait.Placer.html http://doc.rust-lang.org/nightly/core/ops/trait.Placer.html
- acconsta 11y agoJust to be clear, the C++ equivalent of Box is unique_ptr. I don't see equivalents of new and delete, but I might be missing them. I see Box::from_raw and core::ops::Placer. Is that what you mean by in-place boxing? But now there are two problems: You have to manually allocate a buffer of the right size, check if the pointer is null, and box it. For every allocation. Forget a null check? Allocate too small a buffer? Guess what, you've got undefined behavior! It also doesn't generalize to other parts of the standard library. Did you want to use Rust's built in containers in your kernel? Well, sorry, you're going to have to write your own ones that don't panic on allocation failure.
- Manishearth 11y ago> Just to be clear, the C++ equivalent of Box is unique_ptr. Yeah, I mean that it's used like `new` is. We don't have the notion of constructors. > You have to manually allocate a buffer of the right size, check if the pointer is null, and box it. For every allocation. No. With the placement API, you'll be able to use the `box` syntax (which at the moment just supports `Box`, which has OOM issues) for your custom wrapper (i.e., `Box` with OOM support, or whatever). What `box` gives us is that `let x = box make_inner()` would have the returned value of `make_inner()` written directly to the heap location. (Unlike `let x = Box::new(make_inner())`, which may be a move of `make_inner()`) Placer is a part of this (https://github.com/rust-lang/rfcs/blob/master/text/0809-box-and-in-for-stdlib.md https://github.com/rust-lang/rfcs/blob/master/text/0809-box-... is the whole design). from_raw isn't, that's just a useful API for FFI. > Did you want to use Rust's built in containers in your kernel? Well, sorry, you're going to have to write your own ones that don't panic on allocation failure. When you're writing a kernel you're not supposed to be using the standard library at all, just libcore (which exists for this purpose). The same issue exists in C++.
- dbaupp 11y ago1. Yes, as I just said, that's essentially libcore. It doesn't literally offer working dynamic allocations, but I don't see how a cross-platform and cross-use-case freestanding library can do that, too many different environments/constraints to encode an built-in allocator. In fact, core says nothing about how allocation has to work or even if it needs to exist. The rustc distribution has the additional `alloc` and `collections` crates layered on top of `core`, which offers some extra functionality when allocation does exist (without assuming other OS support, like IO), and the allocator used is pluggable. (Do you have an example of a standards complaint C++ freestanding standard library?) 2. There will be some way to do this long term. I can offer no guarantees that it will be what's there right now, but something will exist. This is also something that has been on the radar for a long time, but isn't core to the language. In any case, what is in core is essentially just optimising stack usage, all of the sematics it offers can be done with normal function calls, e.g. `Box::new` is literally just a normal function and perfectly implementable in any environment, if you have some allocation routine (the compiler/language doesn't need to know about either). So the unstable libcore functionality is important and desirable, but not killer. There's also the meaning of "allocators" as in allowing `Vec` (etc.) to use some other algorithm than what Rust does by default; again something desirable and been on the radar for a while, but not key to the language. I don't think this needs any new language features, just people to explore the library space. (NB. this is different to what libcore offers now, which is basically what is called "placement new" in C++.)
- acconsta 11y ago>(Do you have an example of a standards complaint C++ freestanding standard library?) Yes, you've probably heard of libstdc++: https://gcc.gnu.org/onlinedocs/libstdc++/faq.html#faq.what_is_libsupcxx https://gcc.gnu.org/onlinedocs/libstdc++/faq.html#faq.what_i... In other words, freestanding C++ allows you to to use new and delete normally (after providing malloc and free). Note that this a minimum requirement. There's nothing stopping you from using STL containers in embedded systems, and people do (especially now that STL containers can take user defined allocators). It seems like Rust is trying to achieve the same result with libcore. That's good! Thanks for clarifying. But now you lose all the nice parts of the standard library, and probably much of the safety. If you want to use Box, you have to manually do the allocation, check the pointer, and construct the box. Buffer too small? Forget the pointer check? Welcome to undefined behavior. If you want a container, you have to roll your own. If you want a container that takes user-defined allocators, I'm not even sure what'd you do.
- steveklabnik 11y ago> OOM is an abort, not a panic (contrary to the official docs, interestingly) Where in the docs do we say this? I'd like to fix it.
- acconsta 11y agoThe docs (which are generally very good, especially for a language this young) should: 1. Distinguish between abort and panic. 2. Explain that allocation failure is an abort. 3. Explain that a vec can allocate up to twice its nominal size, which is a common cause of OOMs. To their credit, the docs do explain that vec over-allocates, but not that it allocates double the memory. They could also explain that you don't get that memory back unless you ask for it, even if you pop() or remove() elements. Scenario: I'm a new systems programmer excited to try Rust (fast and safe? wow!). I have 8GB of memory in my computer. I push ~4GB of data into a vector so I can process it, something I'm used to doing in Python. POW! My program exits with no error message. What happened? I look at the error handling docs: https://doc.rust-lang.org/book/error-handling.html https://doc.rust-lang.org/book/error-handling.html Well, it sure wasn't a recoverable failure! Must have been a panic. So I look at the docs for vec.push: https://doc.rust-lang.org/std/vec/struct.Vec.html https://doc.rust-lang.org/std/vec/struct.Vec.html "Panics if the number of elements in the vector overflows a usize." A usize is big, right? I didn't overflow that. I guess I could have run out of memory, but I have 8GB. That should be plenty!