-
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
Extending deref/index with ownership transfer: DerefMove, IndexMove, IndexSet #997
Comments
I'd like DerefMove for similar reasons that Servo wants it: servo/servo#3868 Namely, I have an type struct Foo {
handle: my_ffi_crate::foo // foo is a *mut to an uninhabited enum
} and a "borrowed" version of that called struct BFoo<'a> {
handle: my_ffi_crate::bfoo, // bfoo is the *const equivalent of my_ffi_crate::foo
_phantom: PhantomData<&'a Foo>
}
Most of the immutable "accessor" methods of |
A potential problem for the design of |
Missing from this list is also IndexGet, which is the "returns a value" version of Index. |
@gankro what is the difference between your |
Ah I assumed IndexMove took self by-value (given the comparison to DerefMove), but if it takes &mut self, then never mind. |
what does the RFCs being postponed mean? will they simply be back on track as soon as someone has made up a coherent idea of how everything should play together and writes it up? |
@flying-sheep it means "we're not rejecting this RFC because it's bad, now is just not the right time." This happened a lot before 1.0, so we could focus on what needed to ship in 1.0. And yes, if there's a postponed RFC, someone needs to go through and evaluate if it's still valid in its current form, or update it, and re-submit it. |
i’m glad that things like ndarray profit from it. generally, all data structures where “slice” views can’t be rust slices. should i try to update the “remaining Index traits” RFC (#159)? if so:
|
I would say that Perhaps it would be possible to use type inference to decide whether to use let some_array: [i32; 4] = [1, 2, 3, 4];
let some_slice = &some_array[0..3];
let x = some_slice[2]; // x: &i32 (default behavior)
let y: i32 = some_slice[2]; // y: i32 (would use `IndexGet` to pass by value This would more or less follow from the current inference of I do see the potential for compiler confusion in choosing which trait to use for a given operation, but I would at least appreciate a default implementation of I would also like to mention that even though copying-when-indexing is antithetical to C/C++, it is a very common idiom when it comes to Ruby, etc. I don't think this is a weakness in Ruby, but in fact allowing the end-user to use one idiom or other other should help ease the learning curve for people coming from dynamic languages that assume copy. One of my greatest frustrations with the Ruby language is not knowing whether array indexing was happening by-value or by-reference. I think if Rust could acknowledge both of these domains, and allow programmers the explicit option of choosing one or the other, it would go a long way. Edit: thanks @comex for pointing out that I indexed a stack array rather than a slice initially |
@andrewcsmith ( |
TheoryBoth deref and index are for types containing values of other types. They allow to conveniently access contained value. There are two cases currently supported:(1) accept ref, return ref (Deref/Index) There is one case requested for derefs:(3) consume value, return value (DerefMove?,IndexMove?) It's an open question whether or not we need Index counterpart. I personally don't think it will be very useful. There are two more index cases requested:(4) accept ref, return value (IndexGet?) They can be used for collections not actually storing value instances (e.g. bit array setting/returning bools). PracticeNow let's get to practice, and please correct me if I'm wrong about current rules. DerefCurrent deref rulesCurrent manual deref follows simple rules:
Autoderef follows the same rules, but also depends on context:
Proposed deref rulesDeref rules proposed in #178, in short, are: try to move copyables, try to ref non-copyables, otherwise use whatever possible. Proposed manual deref rules:
Proposed autoderef rules:
If rule condition is satisfied, but required trait is not available, then that's a failure and compiler should not try to apply other rules (that's the only thing different from #178) It is possible to support DerefGet/DerefSet, but, then again, it's noticeably ramps complexity and likely not very useful. IndexingCurrent indexing rulesIndexing is more or less follow deref, but is generally simpler, because there is no autoderef counterpart. Current rules for indexing:
Proposed indexing rulesThis extends both #159 and #1129, adds more details and answers some unresolved questions. Main problem is IndexGet. Option of having it adds lots of ambiguity into rules. I propose to make IndexGet and Index/IndexMut/IndexMove mutually exclusive. With mutually exclusive IndexGet and Index/IndexMut/IndexMove, indexing rules actually become pretty simple. For types implementing IndexGet:
For all other types:
Just like with derefs, if rule condition is satisfied, but required trait is not available, then that's a failure and compiler should not try to apply other rules. Note that I'm not sure if we need IndexMove (consume value, return value), but I list it here for completeness sake. Indexing examples// Bits is a tightly packed bits array, implements IndexGet/IndexSet
println!("first bit is: {}", bits[0]); // bits.index_get(0)
bits[1] = true; // bits.index_set(1, true)
bits[2] ^= true; // bits.index_set(2, (bits.index_get(2) ^ true))
let tref = &bits[3] // &bits.index_get(3), reference to temporary
// Array implements Index/IndexMut/IndexSet
println!("first element is: {}", array[0]) // *array.index(0)
let second = &array[1] // array.index(1), ref of second element
let third = &mut array[2] // array.index_mut(2), mutable ref of third element
array[3] = 2; // array.index_set(3, 2)
array[4] += 3; // (*array.index_mut(4)) += 3
// Map implements:
// Index/IndexMut for Index types where Key impl Borrow<Index>
// IndexSet/IndexMove for Index types where Key impl From<Index>
let vref = &array["key"] // array.index("key"), ref of value
let vrefmut = &mut array["key"] // array.index_mut("key"), mutable ref of value
map["new key"] = 1; // map.index_set("new key", 1), creates new entry
map["new key"] += 2 // (*map.index_mut("new key")) += 2, modifies existing entry
return map["key"]; // map.index_move("key"), consumes map
// failures:
&map["missing key"] // map.index("missing key"), fails
&mut map["missing key"] // map.index_mut("missing key"), fails
map["missing key"] += 2 // (*map.index_mut("missing key")) += 2, does not insert value, fails P.S.
|
In the case of indexing, what would happen to would it become obviously it would be nice if it became |
Wow, it's been a while. You're absolutely right, that's a very important use case I missed. I guess rules have to be modified like this to make it work:
I hate to see how complex it gets, but still can't find a more usable solution. |
Given that we're in the I also don't see a clear summary of what is blocking this, in case someone wanted to hack on this, which I think means the design work was never completed? Just trying to get a feel for the situation. |
IndexGet plus IndexSet should not be invoked together, say by A priori, I'd think indexing into collection types should return |
Hello. I'd like this to move forward (especially because of |
What if we do traits like so:
|
Your |
Another variant I imagine: trait DerefMove: Deref {
fn deref_move(&mut self) -> Self::Output;
fn drop_empty(&mut self);
} Reasons:
Two more points:
let a = String::new();
match (a,1) {
(bnd,1) => ... // which type does `bnd` has? `String` or `str`?
...
} One solutions is to make an exception in match ergonomics and deref patterns interaction, so that |
I see, but due to that inability, are |
Alternatively, we split /// trait for moving out of container
trait DerefGet: Deref {
/// This return layout info of data stored by container
fn layout(&self) -> Layout;
/// This returns an actual data pointer. Also, `Unique` from `core::ptr` may be used here.
fn deref_get(&self) -> *const <Self as Deref>::Target;
/// This is intended to dispose **empty** container
unsafe fn drop(*mut self);
}
///trait for moving in a container
trait DerefSet: DerefMut {
/// prepare the memory region
fn prepare(&mut self,layout: Layout);
/// move the value in. Here I would like to use `Unique` too.
fn deref_set(&mut self,*mut <Self as Deref>::Target);
} And the interesting part: ...
let data: Box<[u64]> = ...;
let mut data_moved = Box::new();
*data_moved = *data; // tada! [1]
//let data_moved = box *data //this woun't work as of there is no thing which we `DerefSet` yet. [2]
... [1] What is actually happening is:
[2] If we go that far, we face the need of The problem of fallible allocation (can be probably be found when
A let mut b: Box<[u8;10]> = ...;
*b = [10;10]; //Here we used `DerefSet`, layout passing is optimized out. |
Is this problem unsolvable? |
Whats the status? |
For |
that doesn't work for |
pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
fn index_set(&mut self, index: Idx, value: Self::Output) {
*self.index_mut(index) = value;
}
}
impl<K, Q: ?Sized, V, S> IndexMut<&Q> for HashMap<K, V, S>
where
K: Eq + Hash + Borrow<Q>,
Q: Eq + Hash,
S: BuildHasher,
{
fn index_mut(&self, key: &Q) -> &mut V {
self.get_mut(key).expect("no entry found for key")
}
fn index_set(&mut self, index: Idx, value: Self::Output) {
self.insert(index, value);
}
} |
That's not the problem with
Did you mean, fn index_set(&mut self, index: &Q, value: Self::Output) {
self.insert(index, value);
} Which is illegal? Or you would need to implement |
I did mean that, and hadn't noticed the problem. |
I guess another way we could handle this is to require This would still be a pretty significant limitation on Edit: although this would still force a clone to insert so maybe not |
|
What would the definition of |
As I said DSTs may not implement it, because you can't play with DSTs without pointers anyway. |
You can dereference an unsized box on nightly if you pass the result as argument to a function. This is not something we can just give up on as the Another thing is that you can partially move out of a box even on stable. This is incompatible with |
Could you please post an example? I'm wondering what function can take a dereferenced unsized type as argument. Shouldn't they always stay behind pointers (and if so
|
|
|
BTW, if there's any kind of requirement on the compiler to support these traits properly the 2024 edition would be the right occasion to think about it. |
that doesn't compile currently on playground. but it seems like what you described would apply to the |
Yes, as bjorn3 said, that works The Unstable Book section for the
|
* Thread safe `Clone` method is implemented. * `Index` and `IndexMut` traits are implemented for `usize` indices. * Due to possibly fragmented nature of the underlying pinned vec, and since we cannot return a reference to a temporarily created collection, currently index over a range is not possible. This would be possible with `IndexMove` (see rust-lang/rfcs#997). * Debug trait implementation is made mode convenient, revealing the current len and capacity in addition to the elements. * Upgraded dependencies to pinned-vec (3.7) and pinned-concurrent-col (2.6) versions. * Tests are extended: * concurrent clone is tested. * in addition to concurrent push & read, safety of concurrent extend & read is also tested. * boxcar::Vec is added to the benchmarks.
It is clear that there is a need for the ability to move out of smart pointers and indexable things (
DerefMove
,IndexMove
). The other frequently desired option is toIndexSet
, which would be a special-cased version for indexing in this situation:currently, this is handled via
IndexMut
, but that is sub-optimal, because if thekey
is not already part of themap
, the result is a panic.Basic plan
DerefMove/IndexMove/IndexSet should layer on top of the traits we have now. One needs to be careful here because of subtle interactions with autoref and so forth.
Postponed RFCs
a[b] = c
expression #1129 (map[key] = value
)The text was updated successfully, but these errors were encountered: