Skip to content

Commit 8884ad3

Browse files
authoredApr 3, 2019
Merge pull request #2480 from SimonSapin/liballoc
RFC: Stabilize the alloc crate
2 parents b81d983 + 099c8a9 commit 8884ad3

File tree

1 file changed

+331
-0
lines changed

1 file changed

+331
-0
lines changed
 

‎text/2480-liballoc.md

+331
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
- Feature Name: `liballoc`
2+
- Start Date: 2018-06-14
3+
- RFC PR: [rust-lang/rfcs#2480](https://github.com/rust-lang/rfcs/pull/2480)
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.
10+
11+
This crate provides the subset of the standard library’s functionality that requires
12+
a global allocator (unlike the `core` crate) and an allocation error handler,
13+
but not other operating system 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+
## `no_std` with an allocator
27+
28+
The `core` crate does not assume even the presence of heap memory,
29+
and so it excludes standard library types like `Vec<T>`.
30+
However some environments do have a heap memory allocator
31+
(possibly as `malloc` and `free` C functions),
32+
even if they don’t have files or threads
33+
or something that could be called an operating system or kernel.
34+
Or one could be defined [in a Rust library][wee-alloc]
35+
ultimately backed by fixed-size static byte array.
36+
37+
An intermediate subset of the standard library smaller than “all of `std`
38+
but larger than “only `core`” can serve such environments.
39+
40+
[wee-alloc]: https://github.com/rustwasm/wee_alloc
41+
42+
## Libraries
43+
44+
In 2018 there is a [coordinated push]
45+
toward making `no_std` application compatible with Stable Rust.
46+
As of this writing not all of that work is completed yet.
47+
For example, [`#[panic_implementation]`][panic-impl] is required for `no_std` but still unstable.
48+
So it may seem that this RFC does not unlock anything new,
49+
as `no_std` application still need to be on Nigthly anyway.
50+
51+
[coordinated push]: https://github.com/rust-lang-nursery/embedded-wg/issues/42
52+
[panic-impl]: https://github.com/rust-lang/rust/issues/44489
53+
54+
The immediate impact can be found in the library ecosystem.
55+
Many general-purpose libraries today are compatible with Stable Rust
56+
and also have potential users who ask for them to be compatible with `no_std` environments.
57+
58+
For a library that is fundamentally about using for example TCP sockets or threads,
59+
this may not be possible.
60+
61+
For a library that happens to only use parts of `std` that are also in `core`
62+
(and are willing to commit to keep doing so), this is relatively easy:
63+
add `#![no_std]` to the crate, and change `std::` in paths to `core::`.
64+
65+
And here again, there is the intermediate case of a library that needs `Vec<T>`
66+
or something else that involves heap memory, but not other parts of `std` that are not in `core`.
67+
Today, in order to not lose compatibility with Stable,
68+
such a library needs to make compatibility with `no_std` an opt-in feature flag:
69+
70+
```rust
71+
#![no_std]
72+
73+
#[cfg(feature = "no_std")] extern crate alloc;
74+
#[cfg(not(feature = "no_std"))] extern crate std as alloc;
75+
76+
use alloc::vec::Vec;
77+
```
78+
79+
But publishing a library that uses unstable features, even optionally,
80+
comes with the expectation that it will be promptly updated whenever those features change.
81+
Some maintainers are not willing to commit to this.
82+
83+
With this RFC, the library’s code can be simplified to:
84+
85+
```rust
86+
#![no_std]
87+
88+
extern crate alloc;
89+
90+
use alloc::vec::Vec;
91+
```
92+
93+
… and perhaps more importantly,
94+
maintainers can rely on the stability promise made by the Rust project.
95+
96+
97+
# Guide-level explanation
98+
[guide-level-explanation]: #guide-level-explanation
99+
100+
## For libraries
101+
102+
When using `#![no_std]` in a crate, that crate does not implicitly depend on `std`
103+
but depends on `core` instead. For example:
104+
105+
```diff
106+
-use std::cell::RefCell;
107+
+use core::cell::RefCell;
108+
```
109+
110+
APIs that require a memory allocator are not available in `core`.
111+
In order to use them, `no_std` Rust code must explicitly depend on the `alloc` crate:
112+
113+
```rust
114+
#[macro_use] extern crate alloc;
115+
116+
use core::cell::RefCell;
117+
use alloc::rc::Rc;
118+
```
119+
120+
Note: `#[macro_use]` imports the [`vec!`] and [`format!`] macros.
121+
122+
[`vec!`]: https://doc.rust-lang.org/alloc/macro.vec.html
123+
[`format!`]: https://doc.rust-lang.org/alloc/macro.format.html
124+
125+
Like `std` and `core`, this dependency does not need to be declared in `Cargo.toml`
126+
since `alloc` is part of the standard library and distributed with Rust.
127+
128+
The implicit prelude (set of items that are automatically in scope) for `#![no_std]` crates
129+
does not assume the presence of the `alloc` crate, unlike the default prelude.
130+
So such crates may need to import either that prelude or specific items explicitly.
131+
For example:
132+
133+
```rust
134+
use alloc::prelude::*;
135+
136+
// Or
137+
138+
use alloc::string::ToString;
139+
use alloc::vec::Vec;
140+
```
141+
142+
## For programs¹
143+
144+
[¹] … and other roots of a dependency graph, such as `staticlib`s.
145+
146+
Compared to `core`, the `alloc` crate makes two additional requirements:
147+
148+
* A global heap memory allocator.
149+
150+
* An allocation error handler (that is not allowed to return).
151+
This is called for example by `Vec::push`, whose own API is infallible,
152+
when the allocator fails to allocate memory.
153+
154+
`std` provides both of these. So as long as it is present in the dependency graph,
155+
nothing else is required even if some crates of the graph use `alloc` without `std`.
156+
157+
If `std` is not present they need to be defined explicitly,
158+
somewhere in the dependency graph (not necessarily in the root crate).
159+
160+
* [The `#[global_allocator]` attribute][global_allocator], on a `static` item
161+
of a type that implements the `GlobalAlloc` trait,
162+
defines the global allocator. It is stable in Rust 1.28.
163+
164+
* [Tracking issue #51540] propose the `#[alloc_error_handler]` attribute
165+
for a function with signature `fn foo(_: Layout) -> !`.
166+
As of this writing this attribute is implemented but unstable.
167+
168+
[global_allocator]: https://doc.rust-lang.org/nightly/std/alloc/#the-global_allocator-attribute
169+
[Tracking issue #51540]: https://github.com/rust-lang/rust/issues/51540
170+
171+
172+
# Reference-level explanation
173+
[reference-level-explanation]: #reference-level-explanation
174+
175+
The `alloc` crate already exists (marked unstable),
176+
and every public API in it is already available in `std`.
177+
178+
Except for the `alloc::prelude` module, since [PR #51569] the module structure is a subset
179+
of that of `std`: every path that starts with `alloc::` is still valid and point to the same item
180+
after replacing that prefix with `std::` (assuming both crates are available).
181+
182+
The concrete changes proposed by this RFC are:
183+
184+
* Stabilize `extern crate alloc;`
185+
(that is, change `#![unstable]` to `#![stable]` near the top of `src/liballoc/lib.rs`).
186+
187+
* Stabilize the `alloc::prelude` module and its contents
188+
(which is only re-exports of items that are themselves already stable).
189+
190+
* Stabilize the fact that the crate makes no more and no less than
191+
the two requirements/assumptions of a global allocator and an allocation error handler
192+
being provided for it, as described above.
193+
194+
The exact mechanism for [providing the allocation error handler][Tracking issue #51540]
195+
is not stabilized by this RFC.
196+
197+
In particular, this RFC proposes that the presence of a source of randomness
198+
is *not* a requirement that the `alloc` crate can make.
199+
This is contrary to what [PR #51846] proposed,
200+
and means that `std::collections::hash_map::RandomState` cannot be moved into `alloc`.
201+
202+
[Tracking issue #27783] tracks “the `std` facade”:
203+
crates whose contents are re-exported in `std` but also exist separately.
204+
Other such crates have already been moved, merged, or stabilized,
205+
such that `alloc` is the only remaining unstable one.
206+
Therefore #27783 can serve as the tracking issue for this RFC
207+
and can be closed once it is implemented.
208+
209+
[PR #51569]: https://github.com/rust-lang/rust/pull/51569
210+
[PR #51846]: https://github.com/rust-lang/rust/pull/51846
211+
[Tracking issue #27783]: https://github.com/rust-lang/rust/issues/27783
212+
213+
214+
The structure of the standard library is therefore:
215+
216+
* `core`: has (almost) no runtime dependency, every Rust crate is expected to depend on this.
217+
* `alloc`: requires a global memory allocator,
218+
either specified through the `#[global_allocator]` attribute
219+
or provided by the `std` crate.
220+
* `std`: re-exports the contents of `core` and `alloc`
221+
so that non-`no_std` crate do not need care about what’s in what crate between these three.
222+
Depends on various operating system features such as files, threads, etc.
223+
* `proc-macro`: depends on parts of the compiler, typically only used at build-time
224+
(in procedural macro crates or Cargo build scripts).
225+
226+
227+
228+
# Drawbacks
229+
[drawbacks]: #drawbacks
230+
231+
[Tracking issue #27783] is the tracking issue for the `alloc` crate and, historically, some other crates.
232+
Although I could not find much discussion of that, I believe it has been kept unstable so far
233+
because of uncertainty of what the eventual desired crate structure
234+
for the standard library is, given infinite time and resources.
235+
236+
In particular, should we have a single crate with some mechanism for selectively disabling
237+
or enabling some of the crate’s components, depending on which runtime dependencies
238+
are available in targeted environments?
239+
In that world, the `no_std` attribute and standard library crates other than `std`
240+
would be unnecessary.
241+
242+
By stabilizing the `alloc` crate, we commit to having it − and its public API − exist “forever”.
243+
244+
245+
# Rationale and alternatives
246+
[alternatives]: #alternatives
247+
248+
## Single-crate standard library
249+
250+
The `core` and the `no_std` attribute are already stable,
251+
so in a sense it’s already too late for the “pure” version of the vision described above
252+
where `std` really is the only standard library crate that exists.
253+
254+
It may still be [desirable] to regroup the standard library into one crate,
255+
and it is probably still possible.
256+
The `core` crate could be replaced with a set of `pub use` reexport
257+
to maintain compatibility with existing users.
258+
Whatever the eventual status for `core` is,
259+
we can do the same for `alloc`.
260+
[PR #51569] mentioned above also hopes to make this easier.
261+
262+
While we want to leave the possibility open for it,
263+
at the time of this writing there are no concrete plans
264+
for implementing such a standard library crates unification any time soon.
265+
So the only alternative to this RFC seems to be
266+
leaving heap allocation for `no_std` in unstable limbo for the foreseeable future.
267+
268+
[desirable]: https://aturon.github.io/2018/02/06/portability-vision/#the-vision
269+
270+
## Require randomness
271+
272+
[PR #51569] proposed adding a source of randomness to the other requirements
273+
made by the `alloc` crate.
274+
This would allow moving `std::collections::hash_map::RandomState`,
275+
and therefore `HashMap` (which has `RandomState` as a default type parameter),
276+
into `alloc`.
277+
278+
This RFC chooses not to do this because it would make it difficult to use for example `Vec<T>`
279+
in environments where a source of randomness is not easily available.
280+
281+
I hope that the language will eventually make it possible to have `HashMap` in `alloc`
282+
without a default hasher type parameter, and have the same type in `std` with its current default.
283+
284+
Although I am not necessarily in favor
285+
of continuing the increase of the number of crates in the standard library,
286+
another solution for `HashMap` in `no_std` might be another intermediate crate
287+
that depends on `alloc` and adds the randomness source requirement.
288+
289+
Additionally, with this RFC it should be possible to make https://crates.io/crates/hashmap_core
290+
compatible with Stable Rust.
291+
The downside of that crate is that although based on a copy of the same code,
292+
it is a different type incompatible in the type system with `std::collections::HashMap`.
293+
294+
295+
# Prior art
296+
[prior-art]: #prior-art
297+
298+
I am not aware of a mechanism similar to `no_std` in another programming language.
299+
300+
[Newlib] is a C library for “embedded” systems that typically don’t have an operating system.
301+
It does provide a memory allocator through `malloc` and related functions, unconditionally.
302+
303+
[Newlib]: https://sourceware.org/newlib/
304+
305+
306+
# Unresolved questions
307+
[unresolved]: #unresolved-questions
308+
309+
* Did I miss something in [PR #51569] that makes `alloc` not a subset of `std`?
310+
A double-check from someone else would be appreciated.
311+
312+
* Should the crate be renamed before stabilization?
313+
It doesn’t have exclusivity for memory-allocation-related APIs,
314+
since the `core::alloc` module exists.
315+
What really characterizes it is the assumption that a global allocator is available.
316+
The name `global_alloc` was proposed.
317+
(Although the crate doesn’t only contain the global allocator itself.)
318+
319+
* ~Should the `alloc::prelude` module be moved to `alloc::prelude::v1`?
320+
This would make the `alloc` module structure a subset of `std` without exception.
321+
However, since this prelude is not inserted automatically,
322+
it is less likely that we’ll ever have a second version of it.
323+
In that sense it is closer to `std::io::prelude` than `std::prelude::v1`.~
324+
Done in [PR #58933].
325+
326+
* In addition to being a subset of `std`, should the `alloc` crate (by itself)
327+
be a super-set of `core`? That is, should it reexport everything that is defined in `core`?
328+
See [PR #58175] which proposes reexporting `core::sync::atomic` in `alloc::sync`.
329+
330+
[PR #58933]: https://github.com/rust-lang/rust/pull/58933
331+
[PR #58175]: https://github.com/rust-lang/rust/pull/58175

0 commit comments

Comments
 (0)
Please sign in to comment.