|
| 1 | +- Feature Name: `liballoc` |
| 2 | +- Start Date: 2018-06-14 |
| 3 | +- RFC PR: |
| 4 | +- Rust Issue: [rust-lang/rust#27783](https://github.com/rust-lang/rust/issues/27783) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +Stabilize the `alloc` crate, with a module structure matching `std`. |
| 10 | + |
| 11 | +This crate provides the subset of the standard library’s functionality that requires |
| 12 | +a global allocator (unlike the `core` crate) but not other operating system |
| 13 | +capabilities (unlike the `std` crate). |
| 14 | + |
| 15 | + |
| 16 | +# Motivation |
| 17 | +[motivation]: #motivation |
| 18 | + |
| 19 | +## Background: `no_std` |
| 20 | + |
| 21 | +In some environments the `std` crate is not available: |
| 22 | +micro-controllers that don’t have an operating system at all, kernel-space code, etc. |
| 23 | +The `#![no_std]` attribute allows a crate to not link to `std` implicitly, |
| 24 | +using `core` instead with only the subset of functionality that doesn’t have a runtime dependency. |
| 25 | + |
| 26 | +## Use case 1: pushing `no_std` programs toward stable Rust |
| 27 | + |
| 28 | +Programs (or `staticlib`s) that do not link `std` at all may still want to use `Vec<T>` |
| 29 | +or other functionality that requires a memory allocator. |
| 30 | +Blockers for doing so on stable Rust are diminishing: |
| 31 | + |
| 32 | +* [The `#[global_allocator]` attribute][global_allocator] to specify an allocator |
| 33 | + and remove the need for an operating-system-provided one is stable since Rust 1.28. |
| 34 | +* [PR #51607] adds a fallback handling for OOM (allocation failure) conditions |
| 35 | + for when `std` is not available, |
| 36 | + removing the need for programs to define an unstable `oom` lang item themselves. |
| 37 | + |
| 38 | +With this, the only allocation-related blocker is being able to import `Vec` in the first place. |
| 39 | +This RFC proposes stabilizing the current unstable way to do it: `extern crate alloc;` |
| 40 | + |
| 41 | +[global_allocator]: https://doc.rust-lang.org/nightly/std/alloc/#the-global_allocator-attribute |
| 42 | +[PR #51607]: https://github.com/rust-lang/rust/pull/51607 |
| 43 | + |
| 44 | +## Use case 2: making stable libraries `no_std` |
| 45 | + |
| 46 | +Even if a `no_std` program might still require other features that are still unstable, |
| 47 | +it is very common to use libraries from crates.io that have other users. |
| 48 | +Such a library might support stable Rust and use `Vec<T>` |
| 49 | +(or something else that requires a memory allocator) |
| 50 | +but not other operating-sytem functionality. |
| 51 | + |
| 52 | +Today, making such a library possible to use without `std` without breaking stable users |
| 53 | +requires a compile-time flag: |
| 54 | + |
| 55 | +```rust |
| 56 | +#![no_std] |
| 57 | + |
| 58 | +#[cfg(feature = "no_std")] extern crate alloc; |
| 59 | +#[cfg(not(feature = "no_std"))] extern crate std as alloc; |
| 60 | + |
| 61 | +use alloc::vec::Vec; |
| 62 | +``` |
| 63 | + |
| 64 | +With this RFC, this can be simplified to: |
| 65 | + |
| 66 | +```rust |
| 67 | +#![no_std] |
| 68 | + |
| 69 | +extern crate alloc; |
| 70 | + |
| 71 | +use alloc::vec::Vec; |
| 72 | +``` |
| 73 | + |
| 74 | +# Guide-level explanation |
| 75 | +[guide-level-explanation]: #guide-level-explanation |
| 76 | + |
| 77 | +When using `#![no_std]` in a crate, that crate does not implicitly depend on `std` |
| 78 | +but depends on `core` instead. For example: |
| 79 | + |
| 80 | +```diff |
| 81 | +-use std::cell::RefCell; |
| 82 | ++use core::cell::RefCell; |
| 83 | +``` |
| 84 | + |
| 85 | +APIs that require a memory allocator are not available in `core`. |
| 86 | +In order to use them, `no_std` Rust code must explicitly depend on the `alloc` crate: |
| 87 | + |
| 88 | +```rust |
| 89 | +extern crate alloc; |
| 90 | + |
| 91 | +use core::cell::RefCell; |
| 92 | +use alloc::rc::Rc; |
| 93 | +``` |
| 94 | + |
| 95 | +Like `std` and `core`, this dependency does not need to be declared in `Cargo.toml` |
| 96 | +since `alloc` is part of the standard library and distributed with Rust. |
| 97 | + |
| 98 | +The `alloc` crate does not have a prelude (items that are implicitly in scope). |
| 99 | +So its items that are in the `std` prelude must be imported explicitly |
| 100 | +to be used in `no_std` crates: |
| 101 | + |
| 102 | +```rust |
| 103 | +use alloc::vec::Vec; |
| 104 | +``` |
| 105 | + |
| 106 | + |
| 107 | +# Reference-level explanation |
| 108 | +[reference-level-explanation]: #reference-level-explanation |
| 109 | + |
| 110 | +The `alloc` crate already exists (marked unstable), |
| 111 | +and every public API in it is already available in `std`. |
| 112 | + |
| 113 | +[PR #51569] moves them around so that the module structure matches that of `std`, |
| 114 | +and the public APIs become a subset: |
| 115 | +any path that starts with `alloc::` should still be valid and point to the same item |
| 116 | +after replacing that prefix with `std::` (assuming both crates are available). |
| 117 | + |
| 118 | +All that remains is stabilizing the `alloc` crate itself and tweaking its doc-comment. |
| 119 | +(In particular, removing the “not intended for general usage” sentence |
| 120 | +and mention `no_std` instead.) |
| 121 | +Since it is the only remaining unstable crate tracked by [tracking issue #27783], |
| 122 | +that issue can be closed after this RFC is implemented. |
| 123 | + |
| 124 | +The structure of the standard library is therefore: |
| 125 | + |
| 126 | +* `core`: has (almost) no runtime dependency, every Rust crate is expected to depend on this. |
| 127 | +* `alloc`: requires a global memory allocator, |
| 128 | + either specified through the `#[global_allocator]` attribute |
| 129 | + or provided by the `std` crate. |
| 130 | +* `std`: re-exports the contents of `core` and `alloc` |
| 131 | + so that non-`no_std` crate do not need care about what’s in what crate between these three. |
| 132 | + Depends on various operating system features such as files, threads, etc. |
| 133 | +* `proc-macro`: depends on parts of the compiler, typically only used at build-time |
| 134 | + (in procedural macro crates or Cargo build scripts). |
| 135 | + |
| 136 | +[PR #51569]: https://github.com/rust-lang/rust/pull/51569 |
| 137 | +[tracking issue #27783]: https://github.com/rust-lang/rust/issues/27783 |
| 138 | + |
| 139 | + |
| 140 | +# Drawbacks |
| 141 | +[drawbacks]: #drawbacks |
| 142 | + |
| 143 | +[Tracking issue #27783] is the tracking issue for the `alloc` crate and, historically, some other crates. |
| 144 | +Although I could not find much discussion of that, I believe it has been kept unstable so far |
| 145 | +because of uncertainty of uncertainty of what is the eventual desired crate structure |
| 146 | +for the standard library, given infinite time and resources. |
| 147 | + |
| 148 | +In particular, could we have a single crate with some mechanism for selectively disabling |
| 149 | +or enabling some of the crate’s components, depending on which runtime dependencies |
| 150 | +are available in targetted environments? |
| 151 | +In that world, the `no_std` attribute and standard library crates other than `std` |
| 152 | +would be unecessary. |
| 153 | + |
| 154 | +By stabilizing the `alloc` crate, we commit to having it − and its public API − exist “forever”. |
| 155 | + |
| 156 | + |
| 157 | +# Rationale and alternatives |
| 158 | +[alternatives]: #alternatives |
| 159 | + |
| 160 | +The `core` and the `no_std` attribute are already stable, |
| 161 | +so in a sense it’s already too late for the “pure” version of the vision described above |
| 162 | +where `std` really is the only standard library crate that exists. |
| 163 | + |
| 164 | +It may still be [desirable] to regroup the standard library into one crate, |
| 165 | +and it is proably still possible. |
| 166 | +The `core` crate could be replaced with a set of `pub use` reexport |
| 167 | +to maintained compatibility with existing users. |
| 168 | +Whatever the eventual status is for `core` is, |
| 169 | +we can do the same for `alloc`. |
| 170 | +[PR #51569] mentioned above also hopes to make this easier. |
| 171 | + |
| 172 | +While we want to leave the possibility open for it, |
| 173 | +at the time of this writing there are no concrete plans |
| 174 | +for implementing such a standard library crates unification any time soon. |
| 175 | +So the only alternative to this RFC seems to be |
| 176 | +leaving heap allocation for `no_std` in unstable limbo for the forseeable future. |
| 177 | + |
| 178 | +[desirable]: https://aturon.github.io/2018/02/06/portability-vision/#the-vision |
| 179 | + |
| 180 | + |
| 181 | +# Prior art |
| 182 | +[prior-art]: #prior-art |
| 183 | + |
| 184 | +I am not aware of a mechanism similar to `no_std` in another programming language. |
| 185 | + |
| 186 | +[Newlib] is a C library for “embedded” systems that typically don’t have an operating system. |
| 187 | +It does provide a memory allocator through `malloc` and related functions, unconditionally. |
| 188 | + |
| 189 | +[Newlib]: https://sourceware.org/newlib/ |
| 190 | + |
| 191 | + |
| 192 | +# Unresolved questions |
| 193 | +[unresolved]: #unresolved-questions |
| 194 | + |
| 195 | +Did I miss something in [PR #51569] that makes `alloc` not a subset of `std`? |
| 196 | +A double-check from someone else would be appreciated. |
0 commit comments