-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
RFC: Add a standard trait for getting many &mut to places #3100
base: master
Are you sure you want to change the base?
Conversation
I think it would be helpful for the RFC to include an example of implementing this for a type which is indexed by user-defined types, not just comparatively-simple case of arrays, since we trust |
Is there precedent for Also, is it realistic that there would be an Finally, is there sufficient motivation to put this in the standard library instead of a crate? I believe the rust features which started in a crate nursery generally end up with a better design and implementation than otherwise, although this is not a universally held opinion. |
If used without I agree we should explore adding arrayref-like functionality to core, meaning convenient conversion from slices or arrays into multiple arrays and slices, but with an interface resembling arrayref or slice indexing probably.
|
seems to me that |
@burdges I don't quite understand what you mean by "If used without unsafe on slices, then isn't this less flexible than using split_at_mut in a loop or tree-like recursion?". This isn't a way to get multiple I guess an alternative could be to have this be an operator as well, ie |
@adlerd Can you give an example as to how those traits might look? I am having trouble seeing how you would be able to use an unsafe "raw" trait. Even with the current proposal you could just make I can change what the example implementation is, though I don't see how using |
I think that there is. For instance, this sort of construct needs specific knowledge of the inner workings of types. So the trait impl's really need to be in the same "crate" as the types. Meaning that having a 3rd party crate wouldn't really work very well since the for types (like |
There is a handy interface for this type of thing in https://github.com/burdges/lake/blob/master/Xolotl/src/sphinx/slice.rs that avoids annoying the borrow checkers:
We then implement an iterator for your interface using using
As with We'll gain
And some macro could save you specifying the I'd expect There is some case for all these tools to live in core, including tl;dr We do not require new traits to handle slice lifetimes properly, but yes nicer inherent methods would make this more convenient. It's specifically |
Here's what I was thinking to remove pub unsafe trait GetMutRefsRaw<Key, Value> {
type RawEntry;
fn would_overlap(&self, e1: &Self::RawEntry, e2: &Self::RawEntry) -> bool;
// NB. we provide *mut Self instead of &'_ self because we can't soundly
// hold a shared reference to self and a unique reference to some values at
// the same time, even though borrowck won't notice the relationship.
unsafe fn entry_to_ref<'a>(this: *mut Self, entry: Self::RawEntry) -> &'a mut Value;
fn get_entry<'a>(&mut self, key: Key) -> Option<Self::RawEntry>;
}
pub trait GetMutRefs<Key, Value> {
fn get_mut_refs<'a, const N: usize>(&'a mut self, keys: [Key; N])
-> Option<[&'a mut Value; N]>;
}
impl<T, Key, Value> GetMutRefs<Key, Value> for T
where
T: GetMutRefsRaw<Key, Value>,
{ /* ... */ } This is extracted from a proposed implementation at this gist. NB this isn't meant to be suitable for RFC reference implementation; it's more like a proposed crate implementation. |
|
@burdges Thanks for the explanation. Though I don't see how it generalizes to non-slice types (like Finally, I don't see how it solves the general question for user defined types since you cannot rely on |
@adlerd Thank you. I am not convinced that one should need a reference back to the container for overlap checking. Furthermore, in the general case I am pretty sure that a Finally, to the root of your suggestion. Now that I see what you meant I think that I like it better. Though I don't see how the argument about trait bounds applies since there would only really be one trait that you would want to make a bound on in either case. edit: working through updating the RFC I know see why those functions need the receivers (because they are trait functions). |
I'd expect any safe interface you provide for slices reduces to being monotonic. And There is no
Rust's std never provided traits for user defined types previously. I'd think new traits set a higher bar than inherent methods. I do not rule out that perhaps these traits reach this bar, but I've serious doubts given the long struggle over even the entry interface for hashmap, in #1769 and https://internals.rust-lang.org/t/pre-rfc-abandonning-morals-in-the-name-of-performance-the-raw-entry-api/7043 I suppose the entries interface has a harder goal of modifying keys, while you only permit modifying values here, which sounds fair if limited. Interestingly, cuckoo tables admit an entry interface with local indexing similar to or better than slices, meaning it admits modifying keys while indexes remain tied to particular cuckoo tables, either via a runtime equality test of randomness, or else via a compile-time GhostCell-like invariant lifetime trick. In short, this interface is looks pointless for cuckoo table. In this vein, we might explore GhostCell based or inspired entry-like tooling too, likely some |
Why though, my first example within the RFC doc is a clear example as to why you wouldn't want something like that. |
I meant safety reduces to the way slices store data. Yes, the There is a larger issue however, You prohibit slices in In short, it's unclear whether this interfaces provides any speedup for slices, but it's clear this interfaces avoids teaching users about the much more powerful As written, these traits imply an entry-like interface, but if I understand they can only deliver a mutable value interface for slices, I think I'm no stickler for traits having a rigorously consistent meaning, but traits do benefit from some consistency in their meaning, especially traits in std. I'm saying it's really unclear how much consistency of meaning this trait interface provides. |
It seems like I'm currently proposing something similar: rust-lang/rust#83608 |
@Kimundi Yes it does seem that you have. I have been working on this for over a month now (on IRLO and Zulip) so any feedback would be appreciated. I think that a more general solution than just for slices would be best. Finally, you don't need non-mut versions of these since you can already get multiple |
Shouldn't the |
@jan-hudec That I am unsure about, my reaction after reading the book on |
@jan-hudec The rule for @Nokel81 I don't think the functions actually need receivers per se; I put them in just as a little something extra I thought I would improve the trait. I agree that in most cases they won't be needed, but it seems counterproductive to leave them out when doing so provides no benefit other than being potentially a tiny bit misleading, and it's possible they may be useful. In a situation where they are actually useful, the only alternative would be to embed a That said, I think the doc comment on |
Bevy (an ECS game engine) would very much like this sort of functionality. We implemented something similar by hand for one of the most common use cases: grabbing combinations of items in a list in bevyengine/bevy#1763. I'm not sure what the solution would look like, but hopefully seeing more concrete use cases might be helpful. |
This was discussed in the libs meeting today. We feel that this shouldn't be implemented as a trait: we already have inherent Apart than that, we are interested in a simpler version of this proposal which doesn't use a trait. |
@Amanieu would the libs team be more in favour of a proposal for the types in the std-lib that should implement such methods? |
Yes. As a general rule, I feel that all types that have a |
Okay thanks. Final question, should that be a direct PR then? |
I think it should still be an RFC, because even if it's not a trait, it's very clearly setting a precedent. (As in, it'll be an API that people will expect lots of types in std, and even types in crates, to start providing, so it's worth the extra oversight of an RFC. It's not just a one-off thing that'd be on one std type but nowhere else.) |
Sounds good. Shall this one be closed and I open a new one then? |
@Nokel81 I think that's mostly up to you. If you think that the discussion in this thread is valuable for people reading an updated RFC, then updating this one is probably fine. If you think that the discussion here has too much about the trait details and would thus be more of a distraction from the new one, then feel free to close this and open a new one. |
Without an That said, it is not strictly necessary to compare equality of keys to check equality of resolved pointers, is it? Since the safe interface of this RFC performs an O(n^2) check either way, why not check these using the references returned by the unsafe implementation? In other words, the unsafe implementation only returns an array of struct Nonsense;
impl Ord for Nonsense {
fn cmp(&self, other: &Self) -> Ordering {
if rand::random::<bool>() { Ordering::Less } else { Ordering::Greater }
}
} In case the value is a ZST, the check could simply be skipped since there is no overlapping either way. A bonus effect is that it improves performance by comparing pointers of |
If you look at my proposal, then you should be able to tell that such a trait is not necessary even for types that have keys which are user defined types. |
Would it make sense to implement this as a more exotic implementation of the |
@skmendez That can't work, because |
You're right; I think this could work with GATs so that |
The |
maybe use this instead, which allows using a temporary array (no allocator required if the array length is unsafe trait GetMutManyRaw<Key> {
type Value;
type RawEntry;
/// must be usable to sort `RawEntry`s such that for any list of `RawEntry`s
/// there must be at least one pair of overlapping `RawEntry`s that are next
/// to each other in the sorted list if any `RawEntry`s overlap.
fn raw_entry_sort_cmp(&self, a: &Self::RawEntry, b: &Self::RawEntry) -> Ordering;
fn would_overlap(&self, e1: &Self::RawEntry, e2: &Self::RawEntry) -> bool;
unsafe fn entry_to_ref<'a>(*mut self, entry: Self::RawEntry) -> &'a mut Self::Value;
fn get_raw_entry<'a>(&'a mut self, key: Key) -> Option<Self::RawEntry>;
} |
for |
Rendered: https://github.com/Nokel81/rfcs/blob/get-mut-refs/text/0000-get-mut-refs.md
Previous IRLO discussion: https://internals.rust-lang.org/t/add-as-mut-ref-for-slice-or-array/14199/
Previous Zulip discussion: https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/GetMutRefs