|
| 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