Skip to content

Commit 91e44dd

Browse files
committed
Merge branch 'slice-tail-redesign' of https://github.com/kballard/rfcs
2 parents d0f1899 + a0d4344 commit 91e44dd

File tree

1 file changed

+97
-0
lines changed

1 file changed

+97
-0
lines changed

text/0000-slice-tail-redesign.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
- Feature Name: `slice_tail_redesign`
2+
- Start Date: 2015-04-11
3+
- RFC PR: (leave this empty)
4+
- Rust Issue: (leave this empty)
5+
6+
# Summary
7+
8+
Replace `slice.tail()`, `slice.init()` with new methods `slice.split_first()`,
9+
`slice.split_last()`.
10+
11+
# Motivation
12+
13+
The `slice.tail()` and `slice.init()` methods are relics from an older version
14+
of the slice APIs that included a `head()` method. `slice` no longer has
15+
`head()`, instead it has `first()` which returns an `Option`, and `last()` also
16+
returns an `Option`. While it's generally accepted that indexing / slicing
17+
should panic on out-of-bounds access, `tail()`/`init()` are the only
18+
remaining methods that panic without taking an explicit index.
19+
20+
A conservative change here would be to simply change `head()`/`tail()` to return
21+
`Option`, but I believe we can do better. These operations are actually
22+
specializations of `split_at()` and should be replaced with methods that return
23+
`Option<(&T,&[T])>`. This makes the common operation of processing the
24+
first/last element and the remainder of the list more ergonomic, with very low
25+
impact on code that only wants the remainder (such code only has to add `.1` to
26+
the expression). This has an even more significant effect on code that uses the
27+
mutable variants.
28+
29+
# Detailed design
30+
31+
The methods `head()`, `tail()`, `head_mut()`, and `tail_mut()` will be removed,
32+
and new methods will be added:
33+
34+
```rust
35+
fn split_first(&self) -> Option<(&T, &[T])>;
36+
fn split_last(&self) -> Option<(&T, &[T])>;
37+
fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])>;
38+
fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])>;
39+
```
40+
41+
Existing code using `tail()` or `init()` could be translated as follows:
42+
43+
* `slice.tail()` becomes `&slice[1..]`
44+
* `slice.init()` becomes `&slice[..slice.len()-1]` or
45+
`slice.split_last().unwrap().1`
46+
47+
It is expected that a lot of code using `tail()` or `init()` is already either
48+
testing `len()` explicitly or using `first()` / `last()` and could be refactored
49+
to use `split_first()` / `split_last()` in a more ergonomic fashion. As an
50+
example, the following code from typeck:
51+
52+
```rust
53+
if variant.fields.len() > 0 {
54+
for field in variant.fields.init() {
55+
```
56+
57+
can be rewritten as:
58+
59+
```rust
60+
if let Some((_, init_fields)) = variant.fields.split_last() {
61+
for field in init_fields {
62+
```
63+
64+
And the following code from compiletest:
65+
66+
```rust
67+
let argv0 = args[0].clone();
68+
let args_ = args.tail();
69+
```
70+
71+
can be rewritten as:
72+
73+
```rust
74+
let (argv0, args_) = args.split_first().unwrap();
75+
```
76+
77+
(the `clone()` ended up being unnecessary).
78+
79+
# Drawbacks
80+
81+
The expression `slice.split_last().unwrap().1` is more cumbersome than
82+
`slice.init()`. However, this is primarily due to the need for `.unwrap()`
83+
rather than the need for `.1`, and would affect the more conservative solution
84+
(of making the return type `Option<&[T]>`) as well. Furthermore, the more
85+
idiomatic translation is `&slice[..slice.len()-1]`, which can be used any time
86+
the slice is already stored in a local variable.
87+
88+
# Alternatives
89+
90+
Only change the return type to `Option` without adding the tuple. This is the
91+
more conservative change mentioned above. It still has the same drawback of
92+
requiring `.unwrap()` when translating existing code. And it's unclear what the
93+
function names should be (the current names are considered suboptimal).
94+
95+
Just deprecate the current methods without adding replacements. This gets rid of
96+
the odd methods today, but it doesn't do anything to make it easier to safely
97+
perform these operations.

0 commit comments

Comments
 (0)