Skip to content

Commit 4594f0f

Browse files
committed
Fix panic on string slicing error to truncate the string
The string may be arbitrarily long, but we want to limit the panic message to a reasonable length. Truncate the string if it is too long (simply to char boundary). Also add details to the start <= end message. I think it's ok to flesh out the code here, since it's in a cold function.
1 parent c97524b commit 4594f0f

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

src/libcollectionstest/str.rs

+20
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,26 @@ fn test_slice_fail() {
346346
&"中华Việt Nam"[0..2];
347347
}
348348

349+
const LOREM_PARAGRAPH: &'static str = "\
350+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem sit amet dolor \
351+
ultricies condimentum. Praesent iaculis purus elit, ac malesuada quam malesuada in. Duis sed orci \
352+
eros. Suspendisse sit amet magna mollis, mollis nunc luctus, imperdiet mi. Integer fringilla non \
353+
sem ut lacinia. Fusce varius tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec \
354+
tempus vel, gravida nec quam.";
355+
356+
// check the panic includes the prefix of the sliced string
357+
#[test]
358+
#[should_panic(expected="Lorem ipsum dolor sit amet")]
359+
fn test_slice_fail_truncated_1() {
360+
&LOREM_PARAGRAPH[..1024];
361+
}
362+
// check the truncation in the panic message
363+
#[test]
364+
#[should_panic(expected="luctus, im`[...] do not lie on character boundary")]
365+
fn test_slice_fail_truncated_2() {
366+
&LOREM_PARAGRAPH[..1024];
367+
}
368+
349369
#[test]
350370
fn test_slice_from() {
351371
assert_eq!(&"abcd"[0..], "abcd");

src/libcore/str/mod.rs

+21-3
Original file line numberDiff line numberDiff line change
@@ -1591,12 +1591,30 @@ pub trait StrExt {
15911591
fn parse<T: FromStr>(&self) -> Result<T, T::Err>;
15921592
}
15931593

1594+
// truncate `&str` to length at most equal to `max`
1595+
// return `true` if it were truncated, and the new str.
1596+
fn truncate_to_char_boundary(s: &str, mut max: usize) -> (bool, &str) {
1597+
if max >= s.len() {
1598+
(false, s)
1599+
} else {
1600+
while !s.is_char_boundary(max) {
1601+
max -= 1;
1602+
}
1603+
(true, &s[..max])
1604+
}
1605+
}
1606+
15941607
#[inline(never)]
15951608
#[cold]
15961609
fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! {
1597-
assert!(begin <= end);
1598-
panic!("index {} and/or {} in `{}` do not lie on character boundary",
1599-
begin, end, s);
1610+
const MAX_DISPLAY_LENGTH: usize = 256;
1611+
let (truncated, s) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH);
1612+
let ellipsis = if truncated { "[...]" } else { "" };
1613+
1614+
assert!(begin <= end, "begin <= end ({} <= {}) when slicing `{}`{}",
1615+
begin, end, s, ellipsis);
1616+
panic!("index {} and/or {} in `{}`{} do not lie on character boundary",
1617+
begin, end, s, ellipsis);
16001618
}
16011619

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

0 commit comments

Comments
 (0)