Open lachlansneff opened 3 years ago
I feel that situations where you would want to use dynamically allocated data structures without a global allocator are sufficiently rare that we can just tell people to use a global allocator that always panics.
Fixing this the "proper" way would require messing with default generic parameters at the language level which is likely to be very messy to implement.
One scenario where that might be useful would be for crates where a subset of the functionality would be used in embedded contexts. Many data structures are much easier to create with an allocator, but using the data structure without one is perfectly feasible.
I haven't fully fleshed out this design. But for my OpenTimestamps protocol a timestamp proof is basically a tree, and the binary serialization format was designed to be usable in-place in embedded contexts. A copy-on-write API could work kinda like the following:
enum TimestampProof<'a, A = Global> {
Clean(&'a [u8]),
Dirty(Box<InMemoryProof<A>, A>),
}
So in an embedded context, you'd set A
to be a dummy allocator that can't actually allocate, and you'd only be able to create the Clean
variant from some in-memory buffer containing a serialized timestamp.
Can we make global allocator support a set-by-default feature flag of the alloc
crate? Arguably the ideal would be most of the alloc stuff being in core
, with a globalalloc
crate. But since we can't change the name now, a feature flag might be the least confusing way to do it.
I feel that situations where you would want to use dynamically allocated data structures without a global allocator are sufficiently rare that we can just tell people to use a global allocator that always panics.
Fixing this the "proper" way would require messing with default generic parameters at the language level which is likely to be very messy to implement.
Recently (and by that I mean January) C++'s Study Group 14 reviewed a paper which proposed the removal on the freestanding requirement for the global replaceable operator new to be defined by the implementation. One of the rationales was there are circumstances where an allocator is available, but that there is no single allocator which is suitable to be used as the default global, for example in operating systems. I'd argue the same principle happens here. Maybe if there is no global allocator, and the std crate is not linked, alloc::alloc::alloc panics (and define Global's alloc function to call alloc::alloc::alloc)
Defaulting to all allocations panicking seems like too much of a footgun to me. However it is possible to opt into this behavior today by defining a #[global_allocator]
as Amanieu mentioned. If this choice is common enough it could even be a crate on crates.io
Requiring the user to provide a default, even a panicking one, seems like it could cause more issues. What if the crate gets enabled when it doesn't need to be? rust's stdlib should provide a fallback when neither a global_allocator is available, nor is libstd linked. With the aforementioned C++ paper, it wouldn't be required that a global operator new is available to make use of standard collections (if the implementation chose to otherwise provide them).
Note that C++'s freestanding proposal doesn't include any of the standard container types like std::vector
. So it is closer to Rust's core
than alloc
.
Indeed, though they are permitted to exist
"Freestanding implementations support an implementation-defined subset of the headers, which include at least ...".
Notably, the proposal to remove the requirement of the operator new does not require the implementation not support any of the container headers, or headers like <memory>
.
So its like core, but it can also provide similar functionality to alloc, and doesn't need a global allocation function necessarily.
Is it possible to remove the default Global
allocator from collections in the alloc
crate in a new edition?
Or, I wonder if it's possible to drop these errors down to link time, so that if you didn't actually use the Global
allocator, you wouldn't need a #[global_allocator]
.
Fixing this the "proper" way would require messing with default generic parameters at the language level which is likely to be very messy to implement.
Why would the following not work out of the box (pun intended)?
// std::boxed::Box
type Box<T, A = Global> = core::boxed::Box<T, A>;
I hope it's not mandatory. I want to program without ambient authority, full stop. (Similarly I would want to use https://github.com/bytecodealliance/cap-std, though that addresses a very different sort of ambient authority.)
Echoing @Ericson2314, I've just switched to nightly so I can use alloc_api
both to target embedded systems and to program without ambient authority. What would be needed to make this feasible?
@shlevy Maybe like https://github.com/rust-lang/rust/pull/84266 another CFG axis can be added to remove Global
and friends (and hopefully they both become Cargo features).
The downside of not moving
Box<T, A>
,Vec<T, A>
, and other allocator generic collections into libcore is that to use them, you have to import the alloc crate. This has the side effect of not building unless you specify a#[global_allocator]
and an#[alloc_error_handler]
. This seems inconvenient. Thoughts?