Skip to content

Commit 7934eb3

Browse files
committed
liballoc: rewrite on motivation and alloc crate requirements
* Largely rewrite the motivation section to concentrate on the impact on the (crates.io) library ecosystem. * Mention the assumptions/requirements made by the alloc crate: allocator and OOM handler, but not randomness. * Add stabilization of `alloc::prelude` * Discuss alternatives for HashMap in no_std
1 parent bfd899d commit 7934eb3

File tree

1 file changed

+162
-43
lines changed

1 file changed

+162
-43
lines changed

text/0000-liballoc.md

Lines changed: 162 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
- Feature Name: `liballoc`
22
- Start Date: 2018-06-14
3-
- RFC PR:
3+
- RFC PR: [rust-lang/rfcs#2480](https://github.com/rust-lang/rfcs/pull/2480)
44
- Rust Issue: [rust-lang/rust#27783](https://github.com/rust-lang/rust/issues/27783)
55

66
# Summary
77
[summary]: #summary
88

9-
Stabilize the `alloc` crate, with a module structure matching `std`.
9+
Stabilize the `alloc` crate.
1010

1111
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).
12+
a global allocator (unlike the `core` crate) and an allocation error handler,
13+
but not other operating system capabilities (unlike the `std` crate).
1414

1515

1616
# Motivation
@@ -23,34 +23,49 @@ micro-controllers that don’t have an operating system at all, kernel-space cod
2323
The `#![no_std]` attribute allows a crate to not link to `std` implicitly,
2424
using `core` instead with only the subset of functionality that doesn’t have a runtime dependency.
2525

26-
## Use case 1: pushing `no_std` programs toward stable Rust
26+
## `no_std` with an allocator
2727

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:
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.
3136

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-
* [Tracking issue #51540] discusses how to stabilize a way to specify
35-
the handling of OOM (allocation failure) conditions when `std` is not available,
36-
instead of the unstable `oom` lang item.
37+
An intermediate subset of the standard library smaller than “all of `std`
38+
but larger than “only `core`” can serve such environments.
3739

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+
[wee-alloc]: https://github.com/rustwasm/wee_alloc
4041

41-
[global_allocator]: https://doc.rust-lang.org/nightly/std/alloc/#the-global_allocator-attribute
42-
[Tracking issue #51540]: https://github.com/rust-lang/rust/issues/51540
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.
4350

44-
## Use case 2: making stable libraries `no_std`
51+
[coordinated push]: https://github.com/rust-lang-nursery/embedded-wg/issues/42
52+
[panic-impl]: https://github.com/rust-lang/rust/issues/44489
4553

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.
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.
5157

52-
Today, making such a library possible to use without `std` without breaking stable users
53-
requires a compile-time flag:
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:
5469

5570
```rust
5671
#![no_std]
@@ -61,7 +76,11 @@ requires a compile-time flag:
6176
use alloc::vec::Vec;
6277
```
6378

64-
With this RFC, this can be simplified to:
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:
6584

6685
```rust
6786
#![no_std]
@@ -71,9 +90,15 @@ extern crate alloc;
7190
use alloc::vec::Vec;
7291
```
7392

93+
… and perhaps more importantly,
94+
maintainers can rely on the stability promise made by the Rust project.
95+
96+
7497
# Guide-level explanation
7598
[guide-level-explanation]: #guide-level-explanation
7699

100+
## For libraries
101+
77102
When using `#![no_std]` in a crate, that crate does not implicitly depend on `std`
78103
but depends on `core` instead. For example:
79104

@@ -86,41 +111,105 @@ APIs that require a memory allocator are not available in `core`.
86111
In order to use them, `no_std` Rust code must explicitly depend on the `alloc` crate:
87112

88113
```rust
89-
extern crate alloc;
114+
#[macro_use] extern crate alloc;
90115

91116
use core::cell::RefCell;
92117
use alloc::rc::Rc;
93118
```
94119

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+
95125
Like `std` and `core`, this dependency does not need to be declared in `Cargo.toml`
96126
since `alloc` is part of the standard library and distributed with Rust.
97127

98-
The prelude (set of items that are implicitly in scope) for `#![no_std]` crates
128+
The implicit prelude (set of items that are automatically in scope) for `#![no_std]` crates
99129
does not assume the presence of the `alloc` crate, unlike the default prelude.
100-
So in such crates, items from `alloc` always need to be imported explicitly.
130+
So such crates may need to import either that prelude or specific items explicitly.
101131
For example:
102132

103133
```rust
134+
use alloc::prelude::*;
135+
136+
// Or
137+
138+
use alloc::string::ToString;
104139
use alloc::vec::Vec;
105140
```
106141

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+
107171

108172
# Reference-level explanation
109173
[reference-level-explanation]: #reference-level-explanation
110174

111175
The `alloc` crate already exists (marked unstable),
112176
and every public API in it is already available in `std`.
113177

114-
[PR #51569] moves them around so that the module structure matches that of `std`,
115-
and the public APIs become a subset:
116-
any path that starts with `alloc::` should still be valid and point to the same item
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
117180
after replacing that prefix with `std::` (assuming both crates are available).
118181

119-
All that remains is stabilizing the `alloc` crate itself and tweaking its doc-comment.
120-
(In particular, removing the “not intended for general usage” sentence
121-
and mention `no_std` instead.)
122-
Since it is the only remaining unstable crate tracked by [tracking issue #27783],
123-
that issue can be closed after this RFC is implemented.
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+
124213

125214
The structure of the standard library is therefore:
126215

@@ -134,8 +223,6 @@ The structure of the standard library is therefore:
134223
* `proc-macro`: depends on parts of the compiler, typically only used at build-time
135224
(in procedural macro crates or Cargo build scripts).
136225

137-
[PR #51569]: https://github.com/rust-lang/rust/pull/51569
138-
[tracking issue #27783]: https://github.com/rust-lang/rust/issues/27783
139226

140227

141228
# Drawbacks
@@ -148,22 +235,24 @@ for the standard library, given infinite time and resources.
148235

149236
In particular, could we have a single crate with some mechanism for selectively disabling
150237
or enabling some of the crate’s components, depending on which runtime dependencies
151-
are available in targetted environments?
238+
are available in targeted environments?
152239
In that world, the `no_std` attribute and standard library crates other than `std`
153-
would be unecessary.
240+
would be unnecessary.
154241

155242
By stabilizing the `alloc` crate, we commit to having it − and its public API − exist “forever”.
156243

157244

158245
# Rationale and alternatives
159246
[alternatives]: #alternatives
160247

248+
## Single-crate standard library
249+
161250
The `core` and the `no_std` attribute are already stable,
162251
so in a sense it’s already too late for the “pure” version of the vision described above
163252
where `std` really is the only standard library crate that exists.
164253

165254
It may still be [desirable] to regroup the standard library into one crate,
166-
and it is proably still possible.
255+
and it is probably still possible.
167256
The `core` crate could be replaced with a set of `pub use` reexport
168257
to maintained compatibility with existing users.
169258
Whatever the eventual status is for `core` is,
@@ -174,10 +263,34 @@ While we want to leave the possibility open for it,
174263
at the time of this writing there are no concrete plans
175264
for implementing such a standard library crates unification any time soon.
176265
So the only alternative to this RFC seems to be
177-
leaving heap allocation for `no_std` in unstable limbo for the forseeable future.
266+
leaving heap allocation for `no_std` in unstable limbo for the foreseeable future.
178267

179268
[desirable]: https://aturon.github.io/2018/02/06/portability-vision/#the-vision
180269

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+
181294

182295
# Prior art
183296
[prior-art]: #prior-art
@@ -202,3 +315,9 @@ It does provide a memory allocator through `malloc` and related functions, uncon
202315
What really characterizes it is the assumption that a global allocator is available.
203316
The name `global_alloc` was proposed.
204317
(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`.

0 commit comments

Comments
 (0)