You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add get and get_unchecked to *const [T] and *mut [T] #60639
If one has a *const [T] and wants a pointer to a specific element of that slice, one needs to go through a reference and index, or cast the pointer to *const T and offset that by the number of elements (which doesn't do a length check, so you need that, too). I propose that we add two new methods to *const [T] and *mut [T]:
changed the title [-]add `get` and `get_unchecked` to `*const [T]`[/-][+]Add `get` and `get_unchecked` to `*const [T]` and `*mut [T]`[/+]on May 8, 2019
use std::collections::BTreeSet;fnuniq_refs<'i,'d:'i,T>(data:&'dmut[T],indices:&'iBTreeSet<usize>,) -> implIterator<Item = &'dmutT> + 'i{let in_bounds_indices = indices.range(0..data.len());
in_bounds_indices.map(move |&i| {// I copied this from a Stack Overflow answer // without reading the text that explains why this is safeunsafe{let r = data.get_unchecked_mut(i);let p:*mutT = r;&mut*p
}})}
Miri detects that this usage triggers undefined behavior:
use std::iter::FromIterator;fnmain(){letmut scores = vec![1,2,3];let indices = BTreeSet::from_iter(vec![0,2]);let selected_scores:Vec<_> = uniq_refs(&mut scores,&indices).collect();for score in selected_scores {*score += 1;}}
I wanted to make use of get_unchecked_mut, but that function requires unique access to data. That's invalid here because there's an outstanding mutable reference to a T from inside the slice. The non-UB form of this involves getting a raw pointer to the data and performing offsets:
It could use wrapping_offset, though. Then it could be safe.
But then people would have a performance incentive to not use the clearer and more convenient method. And what for? If one uses this method they'll almost always go on to access memory through the resulting pointer, and then they'd almost certainly have UB anyway if the index is too large or goes otherwise out of bounds. While wrapping_offset does allow "allocation crossing" with a suitably crafted offset, this is extremely rare and so I don't see why we should prioritize it in a convenience method.
But then people would have a performance incentive to not use the clearer and more convenient method.
If they want the performance, wouldn't they use get_unchecked anyway? Seems like if you care about whatever LLVM gets from knowing there is no overflow, you'll also want no bounds check.
Put differently, what is even the point of havingget if it is not safe?
It's a raw pointer, I don't think we are making any such requirements. Both pointer and integer can be just about anything that a *mut T or usize can be.
So, in terms of implementing this as an unstable feature... the main problem is that we want these to be inherent methods on *const [T], right? AFAIK this needs some rustc magic and lang items as those types are not defined in any crate, and thus cannot usually get inherent methods?
Though in this case it may be more difficult due to the involved generics, so you should look at the implementation of the magic for slices for inspiration:
#71082 does all the hard parts here -- now adding more raw slice methods (like indexing) is a libcore-only patch, rustc does not need any further changes.
As far as I am concerned, mostly due to lack of imagination -- I figured if you'd want checks you'd use references, not raw pointers. The return type Option<*const T> is also strange, note that this one cannot be enum-niche-optimized.
Activity
shepmaster commentedon May 8, 2019
My specific case would have been for
*mut [T]— is that deliberately omitted?oli-obk commentedon May 8, 2019
no, I'm just lazy and didn't want to type everything twice. There should be an equivalent of all the above for
*mut [T][-]add `get` and `get_unchecked` to `*const [T]`[/-][+]Add `get` and `get_unchecked` to `*const [T]` and `*mut [T]`[/+]Stargateur commentedon May 8, 2019
Pardon me if It's stupid but why not just let the user go from
*const [T]to&[T]?Is that really to long to write ? Also, want to ask, when do you need a
*const [T]?shepmaster commentedon May 8, 2019
The original context was this code:
Miri detects that this usage triggers undefined behavior:
I wanted to make use of
get_unchecked_mut, but that function requires unique access todata. That's invalid here because there's an outstanding mutable reference to aTfrom inside the slice. The non-UB form of this involves getting a raw pointer to the data and performing offsets:With the proposed functions, that code could look like
RalfJung commentedon May 9, 2019
Yeah, this seems reasonable. However, I think it should be seen in the context of our general need for a better story for raw pointers.
scottmcm commentedon May 12, 2019
Wouldn't that need to be
unsafefor the same reason asoffset?oli-obk commentedon May 12, 2019
Since it would be bounds-checked, you can't overflow, so
getshould be safeoli-obk commentedon May 12, 2019
Ah no, you're right. One can create a wrong slice that would overflow for some indices...
RalfJung commentedon May 12, 2019
It could use
wrapping_offset, though. Then it could be safe.Stargateur commentedon May 12, 2019
just to note that in C, it's UB to create a out of bound pointer of more than len, for exemple:
Look like Rust allow this, but use raw pointer is often use with C binding, I just through this could be useful to know.
hanna-kruppe commentedon May 12, 2019
But then people would have a performance incentive to not use the clearer and more convenient method. And what for? If one uses this method they'll almost always go on to access memory through the resulting pointer, and then they'd almost certainly have UB anyway if the index is too large or goes otherwise out of bounds. While wrapping_offset does allow "allocation crossing" with a suitably crafted offset, this is extremely rare and so I don't see why we should prioritize it in a convenience method.
RalfJung commentedon May 12, 2019
If they want the performance, wouldn't they use
get_uncheckedanyway? Seems like if you care about whatever LLVM gets from knowing there is no overflow, you'll also want no bounds check.Put differently, what is even the point of having
getif it is not safe?4 remaining items
RalfJung commentedon May 24, 2019
It's a raw pointer, I don't think we are making any such requirements. Both pointer and integer can be just about anything that a
*mut Torusizecan be.RalfJung commentedon Mar 25, 2020
So, in terms of implementing this as an unstable feature... the main problem is that we want these to be inherent methods on
*const [T], right? AFAIK this needs some rustc magic and lang items as those types are not defined in any crate, and thus cannot usually get inherent methods?oli-obk commentedon Mar 25, 2020
Yea, we need to add the same kind of magic that the integer types have in
rust/src/libcore/num/mod.rs
Lines 2380 to 2381 in 1572c43
Though in this case it may be more difficult due to the involved generics, so you should look at the implementation of the magic for slices for inspiration:
rust/src/libcore/slice/mod.rs
Lines 56 to 58 in 1572c43
RalfJung commentedon Apr 15, 2020
#71082 does all the hard parts here -- now adding more raw slice methods (like indexing) is a libcore-only patch, rustc does not need any further changes.
RalfJung commentedon Jul 5, 2020
#73986 should resolve this.
Rollup merge of rust-lang#73986 - RalfJung:raw-slice-as-ptr, r=sfackler
Rollup merge of rust-lang#73986 - RalfJung:raw-slice-as-ptr, r=sfackler
Rollup merge of rust-lang#73986 - RalfJung:raw-slice-as-ptr, r=sfackler
Rollup merge of rust-lang#73986 - RalfJung:raw-slice-as-ptr, r=sfackler
Rollup merge of rust-lang#73986 - RalfJung:raw-slice-as-ptr, r=sfackler
Rollup merge of rust-lang#73986 - RalfJung:raw-slice-as-ptr, r=sfackler
Rollup merge of rust-lang#73986 - RalfJung:raw-slice-as-ptr, r=sfackler
WaffleLapkin commentedon May 30, 2021
The checked version,
wasn't implemented. Why?
RalfJung commentedon May 30, 2021
As far as I am concerned, mostly due to lack of imagination -- I figured if you'd want checks you'd use references, not raw pointers. The return type
Option<*const T>is also strange, note that this one cannot be enum-niche-optimized.