Skip to content

Commit 08d5b23

Browse files
authoredJan 23, 2025
Rollup merge of #135073 - joshtriplett:bstr, r=BurntSushi
Implement `ByteStr` and `ByteString` types Approved ACP: rust-lang/libs-team#502 Tracking issue: #134915 These types represent human-readable strings that are conventionally, but not always, UTF-8. The `Debug` impl prints non-UTF-8 bytes using escape sequences, and the `Display` impl uses the Unicode replacement character. This is a minimal implementation of these types and associated trait impls. It does not add any helper methods to other types such as `[u8]` or `Vec<u8>`. I've omitted a few implementations of `AsRef`, `AsMut`, and `Borrow`, when those would be the second implementation for a type (counting the `T` impl), to avoid potential inference failures. We can attempt to add more impls later in standalone commits, and run them through crater. In addition to the `bstr` feature, I've added a `bstr_internals` feature for APIs provided by `core` for use by `alloc` but not currently intended for stabilization. This API and its implementation are based *heavily* on the `bstr` crate by Andrew Gallant (`@BurntSushi).` r? `@BurntSushi`
2 parents 99768c8 + 865471f commit 08d5b23

File tree

13 files changed

+1412
-14
lines changed

13 files changed

+1412
-14
lines changed
 

‎library/alloc/src/bstr.rs

Lines changed: 702 additions & 0 deletions
Large diffs are not rendered by default.

‎library/alloc/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@
102102
#![feature(async_fn_traits)]
103103
#![feature(async_iterator)]
104104
#![feature(box_uninit_write)]
105+
#![feature(bstr)]
106+
#![feature(bstr_internals)]
105107
#![feature(clone_to_uninit)]
106108
#![feature(coerce_unsized)]
107109
#![feature(const_eval_select)]
@@ -228,6 +230,8 @@ mod boxed {
228230
pub use std::boxed::Box;
229231
}
230232
pub mod borrow;
233+
#[unstable(feature = "bstr", issue = "134915")]
234+
pub mod bstr;
231235
pub mod collections;
232236
#[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))]
233237
pub mod ffi;

‎library/core/src/bstr.rs

Lines changed: 581 additions & 0 deletions
Large diffs are not rendered by default.

‎library/core/src/clone.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,16 @@ unsafe impl CloneToUninit for crate::ffi::CStr {
311311
}
312312
}
313313

314+
#[unstable(feature = "bstr", issue = "134915")]
315+
unsafe impl CloneToUninit for crate::bstr::ByteStr {
316+
#[inline]
317+
#[cfg_attr(debug_assertions, track_caller)]
318+
unsafe fn clone_to_uninit(&self, dst: *mut u8) {
319+
// SAFETY: ByteStr is a `#[repr(transparent)]` wrapper around `[u8]`
320+
unsafe { self.as_bytes().clone_to_uninit(dst) }
321+
}
322+
}
323+
314324
/// Implementations of `Clone` for primitive types.
315325
///
316326
/// Implementations that cannot be described in Rust

‎library/core/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@
111111
#![feature(array_ptr_get)]
112112
#![feature(asm_experimental_arch)]
113113
#![feature(bigint_helper_methods)]
114+
#![feature(bstr)]
115+
#![feature(bstr_internals)]
114116
#![feature(const_carrying_mul_add)]
115117
#![feature(const_eval_select)]
116118
#![feature(core_intrinsics)]
@@ -336,6 +338,8 @@ pub mod ascii;
336338
pub mod asserting;
337339
#[unstable(feature = "async_iterator", issue = "79024")]
338340
pub mod async_iter;
341+
#[unstable(feature = "bstr", issue = "134915")]
342+
pub mod bstr;
339343
pub mod cell;
340344
pub mod char;
341345
pub mod ffi;

‎library/core/tests/bstr.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#![feature(bstr)]
2+
3+
use core::ByteStr;
4+
5+
#[test]
6+
fn test_debug() {
7+
assert_eq!(
8+
r#""\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff""#,
9+
format!("{:?}", ByteStr::new(b"\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff")),
10+
);
11+
}
12+
13+
#[test]
14+
fn test_display() {
15+
let b1 = ByteStr::new("abc");
16+
let b2 = ByteStr::new(b"\xf0\x28\x8c\xbc");
17+
18+
assert_eq!(&format!("{b1}"), "abc");
19+
assert_eq!(&format!("{b2}"), "�(��");
20+
21+
assert_eq!(&format!("{b1:<7}!"), "abc !");
22+
assert_eq!(&format!("{b1:>7}!"), " abc!");
23+
assert_eq!(&format!("{b1:^7}!"), " abc !");
24+
assert_eq!(&format!("{b1:^6}!"), " abc !");
25+
assert_eq!(&format!("{b1:-<7}!"), "abc----!");
26+
assert_eq!(&format!("{b1:->7}!"), "----abc!");
27+
assert_eq!(&format!("{b1:-^7}!"), "--abc--!");
28+
assert_eq!(&format!("{b1:-^6}!"), "-abc--!");
29+
30+
assert_eq!(&format!("{b2:<7}!"), "�(�� !");
31+
assert_eq!(&format!("{b2:>7}!"), " �(��!");
32+
assert_eq!(&format!("{b2:^7}!"), " �(�� !");
33+
assert_eq!(&format!("{b2:^6}!"), " �(�� !");
34+
assert_eq!(&format!("{b2:-<7}!"), "�(��---!");
35+
assert_eq!(&format!("{b2:->7}!"), "---�(��!");
36+
assert_eq!(&format!("{b2:-^7}!"), "-�(��--!");
37+
assert_eq!(&format!("{b2:-^6}!"), "-�(��-!");
38+
39+
assert_eq!(&format!("{b1:<2}!"), "abc!");
40+
assert_eq!(&format!("{b1:>2}!"), "abc!");
41+
assert_eq!(&format!("{b1:^2}!"), "abc!");
42+
assert_eq!(&format!("{b1:-<2}!"), "abc!");
43+
assert_eq!(&format!("{b1:->2}!"), "abc!");
44+
assert_eq!(&format!("{b1:-^2}!"), "abc!");
45+
46+
assert_eq!(&format!("{b2:<3}!"), "�(��!");
47+
assert_eq!(&format!("{b2:>3}!"), "�(��!");
48+
assert_eq!(&format!("{b2:^3}!"), "�(��!");
49+
assert_eq!(&format!("{b2:^2}!"), "�(��!");
50+
assert_eq!(&format!("{b2:-<3}!"), "�(��!");
51+
assert_eq!(&format!("{b2:->3}!"), "�(��!");
52+
assert_eq!(&format!("{b2:-^3}!"), "�(��!");
53+
assert_eq!(&format!("{b2:-^2}!"), "�(��!");
54+
}

‎library/std/src/bstr.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//! The `ByteStr` and `ByteString` types and trait implementations.
2+
3+
#[unstable(feature = "bstr", issue = "134915")]
4+
pub use alloc::bstr::{ByteStr, ByteString};

‎library/std/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,8 @@
320320
// Library features (core):
321321
// tidy-alphabetical-start
322322
#![feature(array_chunks)]
323+
#![feature(bstr)]
324+
#![feature(bstr_internals)]
323325
#![feature(c_str_module)]
324326
#![feature(char_internals)]
325327
#![feature(clone_to_uninit)]
@@ -581,6 +583,8 @@ pub mod f64;
581583
pub mod thread;
582584
pub mod ascii;
583585
pub mod backtrace;
586+
#[unstable(feature = "bstr", issue = "134915")]
587+
pub mod bstr;
584588
pub mod collections;
585589
pub mod env;
586590
pub mod error;

‎src/tools/linkchecker/main.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,29 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[
5050
("alloc/slice/trait.Concat.html", &["#method.concat"]),
5151
("alloc/slice/index.html", &["#method.concat", "#method.join"]),
5252
("alloc/vec/struct.Vec.html", &["#method.sort_by_key", "#method.sort_by_cached_key"]),
53+
("alloc/bstr/struct.ByteStr.html", &[
54+
"#method.to_ascii_uppercase",
55+
"#method.to_ascii_lowercase",
56+
"core/slice::sort_by_key",
57+
"core\\slice::sort_by_key",
58+
"#method.sort_by_cached_key",
59+
"#method.sort_by_key"
60+
]),
61+
("alloc/bstr/struct.ByteString.html", &[
62+
"#method.to_ascii_uppercase",
63+
"#method.to_ascii_lowercase",
64+
"core/slice::sort_by_key",
65+
"core\\slice::sort_by_key",
66+
"#method.sort_by_cached_key",
67+
"#method.sort_by_key"
68+
]),
69+
("core/bstr/struct.ByteStr.html", &[
70+
"#method.to_ascii_uppercase",
71+
"#method.to_ascii_lowercase",
72+
"core/bstr/slice::sort_by_key",
73+
"core\\bstr\\slice::sort_by_key",
74+
"#method.sort_by_cached_key"
75+
]),
5376
("core/primitive.str.html", &["#method.to_ascii_uppercase", "#method.to_ascii_lowercase"]),
5477
("core/primitive.slice.html", &["#method.to_ascii_uppercase", "#method.to_ascii_lowercase",
5578
"core/slice::sort_by_key", "core\\slice::sort_by_key",

‎tests/ui/associated-types/associated-types-in-ambiguous-context.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ LL | type X = std::ops::Deref::Target;
4040
|
4141
help: use fully-qualified syntax
4242
|
43+
LL | type X = <ByteStr as Deref>::Target;
44+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
45+
LL | type X = <ByteString as Deref>::Target;
46+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4347
LL | type X = <CString as Deref>::Target;
4448
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
4549
LL | type X = <IoSlice<'_> as Deref>::Target;
4650
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
47-
LL | type X = <IoSliceMut<'_> as Deref>::Target;
48-
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
49-
LL | type X = <OsString as Deref>::Target;
50-
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5151
and N other candidates
5252

5353
error: aborting due to 5 previous errors

‎tests/ui/consts/too_generic_eval_ice.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ LL | [5; Self::HOST_SIZE] == [6; 0]
3232
= help: the following other types implement trait `PartialEq<Rhs>`:
3333
`&[T]` implements `PartialEq<Vec<U, A>>`
3434
`&[T]` implements `PartialEq<[U; N]>`
35+
`&[u8; N]` implements `PartialEq<ByteStr>`
36+
`&[u8; N]` implements `PartialEq<ByteString>`
37+
`&[u8]` implements `PartialEq<ByteStr>`
38+
`&[u8]` implements `PartialEq<ByteString>`
3539
`&mut [T]` implements `PartialEq<Vec<U, A>>`
3640
`&mut [T]` implements `PartialEq<[U; N]>`
37-
`[T; N]` implements `PartialEq<&[U]>`
38-
`[T; N]` implements `PartialEq<&mut [U]>`
39-
`[T; N]` implements `PartialEq<[U; N]>`
40-
`[T; N]` implements `PartialEq<[U]>`
41-
and 3 others
41+
and 11 others
4242

4343
error: aborting due to 4 previous errors
4444

‎tests/ui/inference/issue-72616.stderr

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ LL | if String::from("a") == "a".try_into().unwrap() {}
66
| |
77
| type must be known at this point
88
|
9-
= note: multiple `impl`s satisfying `String: PartialEq<_>` found in the `alloc` crate:
10-
- impl PartialEq for String;
11-
- impl<'a, 'b> PartialEq<&'a str> for String;
12-
- impl<'a, 'b> PartialEq<Cow<'a, str>> for String;
13-
- impl<'a, 'b> PartialEq<str> for String;
9+
= note: cannot satisfy `String: PartialEq<_>`
10+
= help: the following types implement trait `PartialEq<Rhs>`:
11+
`String` implements `PartialEq<&str>`
12+
`String` implements `PartialEq<ByteStr>`
13+
`String` implements `PartialEq<ByteString>`
14+
`String` implements `PartialEq<Cow<'_, str>>`
15+
`String` implements `PartialEq<str>`
16+
`String` implements `PartialEq`
1417
help: try using a fully qualified path to specify the expected types
1518
|
1619
LL | if String::from("a") == <&str as TryInto<T>>::try_into("a").unwrap() {}

‎tests/ui/inference/issue-72690.stderr

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ LL | String::from("x".as_ref());
1515
| ^^^^^^
1616
|
1717
= note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`:
18+
- impl AsRef<ByteStr> for str;
1819
- impl AsRef<OsStr> for str;
1920
- impl AsRef<Path> for str;
2021
- impl AsRef<[u8]> for str;
@@ -41,6 +42,7 @@ LL | |x| String::from("x".as_ref());
4142
| ^^^^^^
4243
|
4344
= note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`:
45+
- impl AsRef<ByteStr> for str;
4446
- impl AsRef<OsStr> for str;
4547
- impl AsRef<Path> for str;
4648
- impl AsRef<[u8]> for str;
@@ -57,6 +59,7 @@ LL | let _ = "x".as_ref();
5759
| ^ ------ type must be known at this point
5860
|
5961
= note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`:
62+
- impl AsRef<ByteStr> for str;
6063
- impl AsRef<OsStr> for str;
6164
- impl AsRef<Path> for str;
6265
- impl AsRef<[u8]> for str;
@@ -83,6 +86,7 @@ LL | String::from("x".as_ref());
8386
| ^^^^^^
8487
|
8588
= note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`:
89+
- impl AsRef<ByteStr> for str;
8690
- impl AsRef<OsStr> for str;
8791
- impl AsRef<Path> for str;
8892
- impl AsRef<[u8]> for str;
@@ -109,6 +113,7 @@ LL | String::from("x".as_ref());
109113
| ^^^^^^
110114
|
111115
= note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`:
116+
- impl AsRef<ByteStr> for str;
112117
- impl AsRef<OsStr> for str;
113118
- impl AsRef<Path> for str;
114119
- impl AsRef<[u8]> for str;
@@ -135,6 +140,7 @@ LL | String::from("x".as_ref());
135140
| ^^^^^^
136141
|
137142
= note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`:
143+
- impl AsRef<ByteStr> for str;
138144
- impl AsRef<OsStr> for str;
139145
- impl AsRef<Path> for str;
140146
- impl AsRef<[u8]> for str;
@@ -161,6 +167,7 @@ LL | String::from("x".as_ref());
161167
| ^^^^^^
162168
|
163169
= note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`:
170+
- impl AsRef<ByteStr> for str;
164171
- impl AsRef<OsStr> for str;
165172
- impl AsRef<Path> for str;
166173
- impl AsRef<[u8]> for str;
@@ -187,6 +194,7 @@ LL | String::from("x".as_ref());
187194
| ^^^^^^
188195
|
189196
= note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`:
197+
- impl AsRef<ByteStr> for str;
190198
- impl AsRef<OsStr> for str;
191199
- impl AsRef<Path> for str;
192200
- impl AsRef<[u8]> for str;
@@ -213,6 +221,7 @@ LL | String::from("x".as_ref());
213221
| ^^^^^^
214222
|
215223
= note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`:
224+
- impl AsRef<ByteStr> for str;
216225
- impl AsRef<OsStr> for str;
217226
- impl AsRef<Path> for str;
218227
- impl AsRef<[u8]> for str;

0 commit comments

Comments
 (0)
Please sign in to comment.