Skip to content

Commit d83fff3

Browse files
committed
Use more specific panic message for &str slicing errors
Separate out of bounds errors from character boundary errors, and print more details for character boundary errors. Example: &"abcαβγ"[..4] thread 'str::test_slice_fail_boundary_1' panicked at 'byte index 4 is not a char boundary; it is inside `α` (bytes 3..5) of `abcαβγ`'
1 parent 127a83d commit d83fff3

File tree

3 files changed

+38
-8
lines changed

3 files changed

+38
-8
lines changed

src/doc/book/strings.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ let hachi = &dog[0..2];
163163
with this error:
164164

165165
```text
166-
thread 'main' panicked at 'index 0 and/or 2 in `忠犬ハチ公` do not lie on
167-
character boundary'
166+
thread 'main' panicked at 'byte index 2 is not a char boundary; it is inside '忠'
167+
(bytes 0..3) of `忠犬ハチ公`'
168168
```
169169

170170
## Concatenation

src/libcollectionstest/str.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -383,17 +383,29 @@ tempus vel, gravida nec quam.";
383383

384384
// check the panic includes the prefix of the sliced string
385385
#[test]
386-
#[should_panic(expected="Lorem ipsum dolor sit amet")]
386+
#[should_panic(expected="byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")]
387387
fn test_slice_fail_truncated_1() {
388388
&LOREM_PARAGRAPH[..1024];
389389
}
390390
// check the truncation in the panic message
391391
#[test]
392-
#[should_panic(expected="luctus, im`[...] do not lie on character boundary")]
392+
#[should_panic(expected="luctus, im`[...]")]
393393
fn test_slice_fail_truncated_2() {
394394
&LOREM_PARAGRAPH[..1024];
395395
}
396396

397+
#[test]
398+
#[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")]
399+
fn test_slice_fail_boundary_1() {
400+
&"abcαβγ"[4..];
401+
}
402+
403+
#[test]
404+
#[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")]
405+
fn test_slice_fail_boundary_2() {
406+
&"abcαβγ"[2..6];
407+
}
408+
397409
#[test]
398410
fn test_slice_from() {
399411
assert_eq!(&"abcd"[0..], "abcd");

src/libcore/str/mod.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -1741,13 +1741,31 @@ fn truncate_to_char_boundary(s: &str, mut max: usize) -> (bool, &str) {
17411741
#[cold]
17421742
fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! {
17431743
const MAX_DISPLAY_LENGTH: usize = 256;
1744-
let (truncated, s) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH);
1744+
let (truncated, s_trunc) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH);
17451745
let ellipsis = if truncated { "[...]" } else { "" };
17461746

1747+
// 1. out of bounds
1748+
if begin > s.len() || end > s.len() {
1749+
let oob_index = if begin > s.len() { begin } else { end };
1750+
panic!("byte index {} is out of bounds of `{}`{}", oob_index, s_trunc, ellipsis);
1751+
}
1752+
1753+
// 2. begin <= end
17471754
assert!(begin <= end, "begin <= end ({} <= {}) when slicing `{}`{}",
1748-
begin, end, s, ellipsis);
1749-
panic!("index {} and/or {} in `{}`{} do not lie on character boundary",
1750-
begin, end, s, ellipsis);
1755+
begin, end, s_trunc, ellipsis);
1756+
1757+
// 3. character boundary
1758+
let index = if !s.is_char_boundary(begin) { begin } else { end };
1759+
// find the character
1760+
let mut char_start = index;
1761+
while !s.is_char_boundary(char_start) {
1762+
char_start -= 1;
1763+
}
1764+
// `char_start` must be less than len and a char boundary
1765+
let ch = s[char_start..].chars().next().unwrap();
1766+
let char_range = char_start .. char_start + ch.len_utf8();
1767+
panic!("byte index {} is not a char boundary; it is inside {:?} (bytes {:?}) of `{}`{}",
1768+
index, ch, char_range, s_trunc, ellipsis);
17511769
}
17521770

17531771
#[stable(feature = "core", since = "1.6.0")]

0 commit comments

Comments
 (0)