This isn't a serious proposal. I've written a little test code:
#![feature(slice_fill)]
pub fn foo1(a: &mut [u8]) {
for i in 0_u8 .. 10 {
a[usize::from(i)] = 0;
}
}
pub fn foo2(a: &mut [u8]) {
for i in (0_u8 .. 10).rev() {
a[usize::from(i)] = 0;
}
}
pub fn foo3(a: &mut [u8]) {
a[.. 10].fill(0);
}
pub fn foo4(a: &mut [u8; 10]) {
for i in 0_u8 .. 10 {
a[usize::from(i)] = 0;
}
}
The asm produced:
foo1:
push rax
test rsi, rsi
je .LBB5_1
mov byte ptr [rdi], 0
cmp rsi, 1
je .LBB5_3
mov byte ptr [rdi + 1], 0
cmp rsi, 2
je .LBB5_6
mov byte ptr [rdi + 2], 0
cmp rsi, 3
je .LBB5_8
mov byte ptr [rdi + 3], 0
cmp rsi, 4
je .LBB5_10
mov byte ptr [rdi + 4], 0
cmp rsi, 5
je .LBB5_12
mov byte ptr [rdi + 5], 0
cmp rsi, 6
je .LBB5_14
mov byte ptr [rdi + 6], 0
cmp rsi, 7
je .LBB5_16
mov byte ptr [rdi + 7], 0
cmp rsi, 8
je .LBB5_18
mov byte ptr [rdi + 8], 0
cmp rsi, 9
je .LBB5_20
mov byte ptr [rdi + 9], 0
pop rax
ret
.LBB5_1:
xor edi, edi
jmp .LBB5_4
.LBB5_3:
mov edi, 1
jmp .LBB5_4
.LBB5_6:
mov edi, 2
jmp .LBB5_4
.LBB5_8:
mov edi, 3
jmp .LBB5_4
.LBB5_10:
mov edi, 4
jmp .LBB5_4
.LBB5_12:
mov edi, 5
jmp .LBB5_4
.LBB5_14:
mov edi, 6
jmp .LBB5_4
.LBB5_16:
mov edi, 7
jmp .LBB5_4
.LBB5_18:
mov edi, 8
jmp .LBB5_4
.LBB5_20:
mov edi, 9
.LBB5_4:
lea rdx, [rip + .L__unnamed_4]
call qword ptr [rip + core::panicking::panic_bounds_check@GOTPCREL]
ud2
foo2:
push rax
cmp rsi, 9
jbe .LBB1_1
mov word ptr [rdi + 8], 0
mov qword ptr [rdi], 0
pop rax
ret
.LBB1_1:
lea rdx, [rip + .L__unnamed_2]
mov edi, 9
call qword ptr [rip + core::panicking::panic_bounds_check@GOTPCREL]
ud2
foo3:
push rax
cmp rsi, 9
jbe .LBB6_1
mov word ptr [rdi + 8], 0
mov qword ptr [rdi], 0
pop rax
ret
.LBB6_1:
lea rdx, [rip + .L__unnamed_5]
mov edi, 10
call qword ptr [rip + core::slice::index::slice_end_index_len_fail@GOTPCREL]
ud2
foo4:
mov word ptr [rdi + 8], 0
mov qword ptr [rdi], 0
ret
The asm of foo1 is kind of terrible because Rust semantics requires the panics to be generated at the first out of bounds in the same order it should appear. foo2 is more reasonable (and it has the same asm of foo3) because here the programmer required to access the array section (or slice) from its end.
foo4 is the best because the compiler knows statically that out of bounds will not happen.
Another possibility is:
pub fn foo5(a: &mut [u8; 10..]) {
for i in 0_u8 .. 10 {
a[usize::from(i)] = 0;
}
}
The semantics of that code is: foo5 is a regular function, no const generics are present here. foo5 gets as arguments a pair of pointer of a + length of a, just like foo1. But the compiler knows the length of a is >= 10, so no bounds tests are present inside that foo5, so the asm is the same as the asm of foo4, it contains no tests.
At the call point you can't pass any slice to foo5, the compiler inserts an automatic length test at the call point if you give it a run-time slice, but the compiler doesn't insert the test if you give it an array of length >= 10. (So there is an implicit cast. I am not sure this is a good idea for Rust, where implicit casts are very rare).
If inside foo5 you try to index a past length 10 or with an index of value unknown at compile-time, then it inserts a run-time test as it does with regular slices. It omits run-time test only if you access a with indexes statically known to be < 10 (as here).
Note that &[u8; 1..] means a non-empty slice. I find several situations where I don't want an input empty slice/array.
I can think of various situations where such kind of arrays could be useful (parsing, etc) to remove some array bound tests and speed up code, but in practice too many kinds of slices and arrays are unacceptable for a reasonable system language... So this isn't a serious proposal.