New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Split the common usage patterns off the Alloc
trait
#50436
Conversation
r? @cramertj (rust_highfive has picked a reviewer for you, use r? to override) |
Assigning to someone on libs-- r? @sfackler |
The `Alloc` trait keeps functions like `alloc`, `dealloc`, `realloc`, etc. and a new `AllocExt` trait provides `alloc_one`, `dealloc_one`, `alloc_array`, etc. While there are some hypothetical benefits from being able to have custom implementation for the latter, it's far from the most common need, and when you need to implement generic wrappers (which I've found to be incredibly common, to the point I've actually started to write a custom derive for that), you still need to implement all of them because the wrappee might be customizing them. OTOH, if an `Alloc` trait implementer does try to do fancy things in e.g. `alloc_one`, they're not guaranteed that `dealloc_one` will be called for that allocation. There are multiple reasons for this: - The helpers are not used consistently. Just one example, `raw_vec` uses a mix of `alloc_array`, `alloc`/`alloc_zeroed`, but only uses `dealloc`. - Even with consistent use of e.g. `alloc_array`/`dealloc_array`, one can still safely convert a `Vec` into a `Box`, which would then use `dealloc`. - Then there are some parts of the API that just don't exist (no zeroed version of `alloc_one`/`alloc_array`) So even though there are actual use cases for specialization of e.g. `alloc_one` (and as a matter of fact, I do have such a need for mozjemalloc), one is better off using a specialized allocator instead. Actually, it's worse than that, in the rust repo, there's exactly one use of `alloc_array`, and no use of `alloc_one`, `dealloc_one`, `realloc_array`, `dealloc_array`. Not even box syntax uses `alloc_one`, it uses `exchange_malloc`, which takes a `size` and `align`. So those functions are more meant as a convenience for clients than for implementers. The `AllocExt` trait is implemented for all types implementing `Alloc` automatically, allowing clients to opt into using its methods, while preventing implementers from shooting themselves in the foot by trying to do fancy things by overriding those methods (and making it easier on people implementing proxy allocators).
FWIW this looks like a good step forward to me. IMO if nobody is using these we should just kill them in the future. Those who want to re-add them should make a point for them. |
As discussed on irc, I think once we have |
…dealloc_array Because generic methods can't exist in trait objects, those Self: Sized bounds were necessary for Alloc to be usable as a trait object. With those methods now being in a separate trait, the bounds are not necessary anymore. In fact, since AllocExt is implemented for all instances of Alloc, the methods are now even available to Alloc trait objects.
Added #50414 on top. |
@alexcrichton what do you think here? This is not the pattern used elsewhere in the standard library for convenience default impls (e.g on Another approach could be adding these as methods as impls on impl Alloc {
pub fn alloc_one<T>(&mut self) -> Result<Unique<T>, AllocErr> { ... }
} |
Oh, I guess the direct impl approach probably wouldn't work in a non-trait object context. |
I'd personally prefer to stick to the pattern we have elsewhere in the standard library which is to leave them all in the original trait. It's true that if they're overridden then wrappers won't guarantee they're called and definitions can't assume they're called either. Despite that though this remains the most ergonomic way to call them (only import one trait instead of two) and it leaves the possibility to implement speedier wrappers, even if it's hard to adhere to |
A downside is that users potentially need to import two traits instead of just one. |
What's the advantage of EDIT: the only utility I see is that |
|
That's indeed very useful. |
I think that with respect to
Having I like the option of requiring that Therefore I am more in favor of deprecation with removal once |
Ping from triage @sfackler! What's the status of this PR? |
I think we want to document the semantics of how these methods interact and leave them in Alloc. |
Ping from triage @glandium! The reviewer asked for some changes, can you do them? |
Per #50436 (comment) and #50436 (comment) , let's just close this. |
The
Alloc
trait keeps functions likealloc
,dealloc
,realloc
,etc. and a new
AllocExt
trait providesalloc_one
,dealloc_one
,alloc_array
, etc. While there are some hypothetical benefits frombeing able to have custom implementation for the latter, it's far from
the most common need, and when you need to implement generic wrappers
(which I've found to be incredibly common, to the point I've actually
started to write a custom derive for that), you still need to implement
all of them because the wrappee might be customizing them.
OTOH, if an
Alloc
trait implementer does try to do fancy things ine.g.
alloc_one
, they're not guaranteed thatdealloc_one
will becalled for that allocation. There are multiple reasons for this:
The helpers are not used consistently. Just one example,
raw_vec
uses a mix of
alloc_array
,alloc
/alloc_zeroed
, but only usesdealloc
.Even with consistent use of e.g.
alloc_array
/dealloc_array
, onecan still safely convert a
Vec
into aBox
, which would then usedealloc
.Then there are some parts of the API that just don't exist (no
zeroed version of
alloc_one
/alloc_array
)So even though there are actual use cases for specialization of e.g.
alloc_one
(and as a matter of fact, I do have such a need formozjemalloc), one is better off using a specialized allocator instead.
Actually, it's worse than that, in the rust repo, there's exactly one
use of
alloc_array
, and no use ofalloc_one
,dealloc_one
,realloc_array
,dealloc_array
. Not even box syntax usesalloc_one
,it uses
exchange_malloc
, which takes asize
andalign
. So thosefunctions are more meant as a convenience for clients than for
implementers.
The
AllocExt
trait is implemented for all types implementingAlloc
automatically, allowing clients to opt into using its methods, while
preventing implementers from shooting themselves in the foot by trying
to do fancy things by overriding those methods (and making it easier on
people implementing proxy allocators).