-
Notifications
You must be signed in to change notification settings - Fork 9
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
Fallible allocation (via Associated Error type) #10
Comments
The current methods don't return a |
@glandium See the first point: make a bunch of |
The first point kind of was not clear about what it means. |
Where the |
I'm not sure how best to go about doing it, but I strongly support Rust adding some support for fallible allocation. Has there already been discussion about different approaches? Would something like a |
I know there are some discussions floating around on github and internals.rlo on fallible allocation, but I cannot provide a link atm., those threads are pretty long and spread all over the platforms.
To me this makes sense, however I'd name them |
Yeah, that would probably be preferable. Has that ship already sailed, though? I think I saw discussions about |
The |
The The Separately, for implementing APIs like let some_layout = Layout::from_size_align(…);
let ptr = some_allocator
.alloc(some_layout)
.unwrap_or_else(|_| std::alloc::handle_alloc_error(some_layout)); To simplify such code, there was discussion in at least https://internals.rust-lang.org/t/pre-rfc-changing-the-alloc-trait/7487 to have APIs like the While adding an
|
Allocators may provide additional information, why an allocation failed. For example a stack allocator could return, that the stack is full, and only
|
This |
@Ericson2314 Alternatively, instead of duplicating all methods again as This allows users to pick whatever they need when they need it, without having to hope that collections and such properly use |
@gnzlbg that is basically what I have done :). I'm almost done with the rebase. I'm going to clean that up and then amend the OP to actually be clear this time! |
I feel that this thread is hopelessly fraught with confusion about fallibility of methods of allocator handles (like |
@SimonSapin Yes I did confuse people, but they are related in my plan. Basically ever layer should be as polymorphic as possible, kicking the fallibility decision up to the final consumer / root crate. |
@Ericson2314 We are not in your head. You need to communicate with people who do not have the context of everything you’re thinking about. |
@SimonSapin indeed I do. Does the OP make sense now? |
Yes, this is better. There’s a downside to this approach: since they require different allocator handle types, you have to pick for any given collection one of fallible or infallible allocation. In today’s Nightly |
That's a big downside. I can imagine that one wants to, for a particular collection, handle fallibility in some part of the code, and consider the collection infallible in others. I don't know how we could solve this. I suppose one option would be some sort of conversions, e.g., allowing |
I think I wrote some |
We solve it by not making fallibility (a relevant) part of the type. Both fallible and infallible methods are always available. The latter are implemented by calling the former, matching, and calling An associated error type is potentially useful if allocator impls want to carry extra information in error values. But IMO making the error type uninhabited is not that useful at all. At the allocator handle level I would prefer to have a convenience wrapper type or separate trait that provides an infallible API signature and takes care of calling |
@SimonSapin No that's a bad plan because then you loose fallibility polymorphism and we're left with the ecosystem split. |
[FWIW I'm not even sure how important the mixing use-case is. If I care about some error, why would I panic on it some of the time and handler it other times? I think a better idiom for robust software is to never use |
I mean the general rust error handling story is:
I don't see why allocation failures should be at all different.
As already happens with |
What is fallibility polymorphism? Why is it important or useful? |
@SimonSapin Thanks for asking, I thought I hit this in point 3 in the new OP, but now I'm remembering more nuance from things I wrote down years ago but not yet in this thread. "fallibility polymorphism" is what I'm calling "allocation failure error type polymorphism". It's the ability to write code, be it collections or "application code" or whatever else with a polymorphic error type, so the code can be used by code that never wants to thinks about allocation failure, code that wants to never panic, and everything in between. If Rust were going 1.0 in 2021, I'd say the collections should always return But Given that the non-
So, contrary to what I wrote in the OP. collections code reuse is not a unique benefit to my plan. (My apologizes, I'll amend.) But there's still the ecosystem split problem with plan 1. Let's assume that we have some libraries, all of which use collections /
( Here are the problems:
|
I think this proposal is a lot more controversial than you seem to assume.
It’s really not. Most code is in functions that do not return a |
Unless I'm misunderstanding @Ericson2314, I think a more clear way to phrase the original suggestion might be something like "library code that is ambivalent about allocation failure should use ?, which should be ergonomic enough." That library code can impl |
Forcing everyone to sprinkle Remember that I’m responding to this, which seems to be the premise for everything else here:
|
@SimonSapin Rust is not pre-1.0, and so I'm not proposing getting rid of those methods. It's not the premise for everything else; in fact it's the opposite. After defining "fallibility polymorphism", I realized it's not the only way to avoid splitting the ecosystem; a single The example situation below the line is suppose to show very explicitly why no error polymorphism / fallibility polymorphism in the presence of those methods is no good. If you like, skip the top part and read that. @scott-maddox Yeah that is a lot closer to what I meant. The whole question of splitting or not splitting the ecosystem hinges on what libraries can do to support both users cases. Even user code using
OK first of all that's still way better than That said, the situation is still far better than even one |
I just want to note that the main reason we have a This would be obviously very painful, e.g., // crate A - edition2018
fn foo() -> Vec<i32>;
// crate B - edition20XY prelude::v2::*
fn bar() {
let x: Vec<i32> = A::foo(); // ERROR: type mismatch: v2::Vec<i32> != v1::Vec<i32>
} probably as painful as upgrading a dependency graph from But if we can't resolve #2 to make type parameters unstable, we might actually have to move the collections to a |
The prelude module only contains I think that
This is at best very misleading phrasing. |
Sorry, breaking changes to the prelude that users get by default would probably be more accurate. |
@gnzlbg Your zulip thread seems positive so far so I hope #2 works out. Even if we would somehow remove those methods it wouldn't be a breaking change to Before this thread meanders somewhere else, does my example above in #10 (comment) about ecosystem splitting make sense? |
Closing for #23 because this thread is indeed messy. |
Following up from https://rust-lang.zulipchat.com/#narrow/stream/197181-t-libs.2Fwg-allocators/topic/Collections.20beyond.20box.2C.20allocation.20fallibility . I've done a preliminary rebase of rust-lang/rust#52420 at https://github.com/QuiltOS/rust/tree/allocator-error (old versions are https://github.com/QuiltOS/rust/tree/allocator-error-old, https://github.com/QuiltOS/rust/tree/allocator-error-old-1, etc.)
If we add an associated
Err
type toAlloc
, we can set it to!
to indicate allocation is infallible (panics, aborts, etc). Allocators should be written with a non-emptyAlloc::Err
, but aAbortAdapter<A>
is provided which callshandle_alloc_error
sofor<A: Alloc> <AbortAdapter<A> as Alloc>::Err = !
.This has many benefits.
We get
try_
and traditional versions of every collections method that supports fallibility it for no extra cost! This is done by threading the abstract error type. We don't need to duplicate the original method, but can simply do:The legacy version would just call the non-
try_
version and panic-free unwrap theResult<T, !>
. Yes we have twice as many methods, but don't have any duplicated logic.Any container (or individual method on the container) that can not handle fallible allocation can require
Err = !
so as not to hold back the alloc-parameterization and fallible-allocation-recovery of the other containers. I.e. this proposal reduces the risk of refactoring the collection in that there's no all-or-nothing bifurcation of outcomes.Polymorphism all the way up. Just as collections are fallibility-polymorphic with the
try_methods
(perhaps they should be renamed to indicate the are not always fallible), other crates using collections can also useA::Err
to also be fallibility-polymorphic. This avoids an ecosystem split between those that want and don't want to manually deal with allocation failure.Note that this seems to impose a downside of each collection having to commit to a fallibility up front. But since
AbortAdapter
can be#[repr(transparent)]
, we can freely cast/borrow between the two.Original unclear version for posterity:
If we add an associated
Err
type toAlloc
, we can set it to!
to indicate allocation is infallible (panics, aborts, etc). This has two purposes:We instantly get
try_
versions of every method that supports it for free by threading a non-empty error type. The legacy version would just call the non-try_
version and unwrap theResult<T, !>
. Yes we have twice as many methods, but don't have any duplicated logic.Any container (or individual method on the container) that can not handle fallible allocation can require
Err = !
so as not to hold back the alloc-parameterization and fallible-allocation-recovery of the other containers.The text was updated successfully, but these errors were encountered: