Skip to content

Commit 955ead9

Browse files
committed
Unsafe Indexing and Slicing
1 parent c7da23a commit 955ead9

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed

active/0000-unsafe-indexing.md

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
- Start Date: 2014-10-13
2+
- RFC PR: (leave this empty)
3+
- Rust Issue: (leave this empty)
4+
5+
# Summary
6+
7+
Add unchecked indexing and slicing operators, accessible via
8+
```array[unsafe n]```.
9+
10+
# Motivation
11+
12+
Currently, unchecked indexing is among the safest unsafe operations
13+
in Rust, as the required invariant (in-boundness) is often satisfied
14+
locally (especially with Rust's aliasing rules, which prevent many of the
15+
usual ways for it to be violated), and even when it is not verifiable it
16+
is desired.
17+
18+
Unfortunately, the current syntax for indexing is something like the ugly
19+
```*a.get_unchecked(9)```, and if you're wrapping individual
20+
operations in unsafe blocks it becomes even uglier
21+
```unsafe { *a.get_unchecked(9) }```.
22+
23+
The ```s[unsafe n]``` seems light enough, while still warning users
24+
of the Sword of Damocles that comes with unchecked indexing.
25+
26+
# Detailed design
27+
28+
Add 4 new operator traits, ```UnsafeIndex```, ```UnsafeSlice```,
29+
```UnsafeIndexMut```, and ```UnsafeSliceMut```, with the following syntax:
30+
31+
* UnsafeIndex/UnsafeIndexMut
32+
```Rust
33+
trait UnsafeIndex<E> {
34+
type R;
35+
unsafe fn index_unchecked<'a>(&'a self, element: &E) -> &'a R;
36+
}
37+
38+
trait UnsafeIndexMut<E> {
39+
type R;
40+
unsafe fn index_unchecked_mut<'a>(&'a mut self,
41+
element: &E) -> &'a mut R;
42+
}
43+
44+
fn my_fn(s: &mut [uint]) {
45+
if s.len < 2 { return; }
46+
// ...
47+
s[unsafe 0] = f(s[unsafe 2]);
48+
}
49+
```
50+
51+
* UnsafeSlice/UnsafeSliceMut
52+
```Rust
53+
trait UnsafeSlice<Idx> {
54+
type S;
55+
unsafe fn as_slice_unchecked<'a>(&'a self) -> &'a S;
56+
unsafe fn slice_from_unchecked<'a>(&'a self, from: Idx) -> &'a S;
57+
unsafe fn slice_to_unchecked<'a>(&'a self, to: Idx) -> &'a S;
58+
unsafe fn slice_unchecked<'a>(&'a self, from: Idx, to: Idx) -> &'a S;
59+
}
60+
61+
trait UnsafeSliceMut<Idx> {
62+
type S;
63+
unsafe fn as_mut_slice_unchecked<'a>(&'a mut self) -> &'a mut S;
64+
unsafe fn slice_from_unchecked_mut<'a>(&'a mut self,
65+
from: Idx) -> &'a mut S;
66+
unsafe fn slice_to_unchecked_mut<'a>(&'a mut self,
67+
to: Idx) -> &'a mut S;
68+
unsafe fn slice_unchecked_mut<'a>(&'a mut self,
69+
from: Idx,to: Idx) -> &'a mut S;
70+
}
71+
72+
fn my_ex_2(s: &mut [uint]) -> uint{
73+
if s.len < 5 { return; }
74+
// ...
75+
let v = calculate(s[unsafe 3..5]);
76+
// ...
77+
mutate(s[unsafe mut 0..1])
78+
}
79+
```
80+
81+
Note that unsafe blocks aren't needed around unsafe indexing/slicing
82+
they "bring their own unsafe bubble" (but they don't
83+
allow unsafe operations within the index, so
84+
```s[unsafe ptr::read(ip)]``` *does* need an unsafe block).
85+
86+
The traits should be implemented for at least ```&T```, ```&mut T```, and
87+
```Vec<T>``` (of course, the ```&T``` does not need to implement the
88+
```Mut``` ones). ```*const T``` and ```*mut T``` should implement the
89+
slice traits, but I'm not sure they should implement the indexing traits,
90+
as indexing unsafe slices involves some subtleties wrt. destructors
91+
(however, the traits taking an ```&'a *T``` should ameliorate the
92+
problem somewhat).
93+
94+
As a case study, here's an implementation of ```insertion_sort``` in terms
95+
of the new functionality (other implementations can be seen in
96+
RFC #365):
97+
98+
```Rust
99+
/// Rotates a slice one element to the right,
100+
/// moving the last element to the first one and all other elements one place
101+
/// forward. (i.e., [0,1,2,3] -> [3,0,1,2])
102+
fn rotate_right<T>(s: &mut [T]) {
103+
let len = s.len();
104+
let s = s.as_raw_mut();
105+
if len == 0 { return; }
106+
107+
unsafe {
108+
let first = s.read(len-1);
109+
s[unsafe mut 1..].copy(s[unsafe ..len-1]);
110+
s.write(0, first);
111+
}
112+
}
113+
114+
fn insertion_sort<T>(v: &mut [T], compare: |&T, &T| -> Ordering) {
115+
let len = v.len();
116+
117+
// 1 <= i < len;
118+
for i in range(1, len) {
119+
// j satisfies: 0 <= j <= i;
120+
let mut j = i;
121+
122+
// find where to insert, we need to do strict <,
123+
// rather than <=, to maintain stability.
124+
// 0 <= j - 1 < len, so j - 1 is in bounds.
125+
// and i is also in bounds
126+
while j > 0 && compare(v[unsafe i], v[unsafe j-1]) == Less {
127+
j-=1;
128+
}
129+
130+
// `i` and `j` are in bounds, so [j, i+1) = [j, i] is valid.
131+
rotate_right(v[unsafe j..i-1]);
132+
}
133+
}
134+
```
135+
136+
# Drawbacks
137+
138+
A new operator set will add complexity to the language (there seems to be
139+
a small combinatorical explosion with ```(Unsafe)?(Index|Slice)(Mut)?)```.
140+
Significantly, there will be another syntax for unsafety, which
141+
unsafety-scanners will need to notice.
142+
143+
In addition, the syntax would be slightly ambigious with non-block
144+
unsafe expressions if they are ever introduced
145+
(```a[unsafe ptr::read(x)]```). Giving this syntax precedence seems
146+
to properly deal with it, especially because actually doing unsafe
147+
operations will give you a clean compile-time error in this case.
148+
149+
# Alternatives
150+
151+
Enabling unchecked indexing via a crate or module attribute is a common
152+
suggestion by game-developers. This has the unfortunate problem of
153+
adding unsafety to all array accesses.
154+
155+
# Unresolved questions
156+
157+
Do we want direct unsafe indexing on raw slices? (Note that
158+
*slicing* raw slices is completely fine and is suggested by this RFC).
159+
160+
Bikeshed: do we want to allow ```s[mut unsafe I]```, or only
161+
```s[unsafe mut I]```

0 commit comments

Comments
 (0)