I wanted to share a subtle syntactic/design observation regarding string slices that I’ve found a bit surprising:
String literals like "hello" are intrinsically references of type &'static str. No & is needed.
However, slices obtained from strings or &str (e.g., my_string_literal[2..6]) have type str (unsized). Since Rust cannot store str standalone, we are required to write:
let slice = &my_string_literal[2..6];
Conceptually, this feels redundant, because a slice is already a reference to a part of a string. Requiring & seems at odds with the mental model that “slice = reference.”
I understand this is a type-system necessity in Rust, but I wonder if there might be room for discussion about a syntax where slices automatically produce &str, aligning syntax with the conceptual model.
I’m curious if others have encountered this and what thoughts the core team has about possibly simplifying this syntax.
There's at least one case in which you wouldn't use the shared-borrow operator &:
let mut my_string = String::from("Hello, world!");
let slice = &mut my_string[2..6];
slice.make_ascii_uppercase();
dbg!(my_string);
&mut str is very nearly useless because it can only manipulate string slices in ways that don't change the number of bytes in the slice, and very few useful operations on UTF-8 strings are guaranteed to keep the byte length the same (as it's often different from the character length). But some such operations do exist, such as consistently replacing one character with another in cases where all the replacements have the same width as the original, and that's how Rust ended up with methods like make_ascii_uppercase (because uppercase ASCII letters have the same byte width as their lowercase equivalents).
For what it's worth, I think it would have been better if Rust's slicing had always followed a "slicing a reference produces a reference with the same mutability" rule – this is in practice what you end up wanting in almost all cases (both for strings and for slices). But changing that rule now would break pretty much every Rust program ever written. In theory, you could do it over an edition, but it would be a much more radical change than any of the previous edition changes due to the amount of code that would have to change.
where the documentation (of std::ptr::addr_of!(expr) – which is the old name for the &raw const expr-expression syntax, so the docs apply equally to &raw const …) states the following
It is still an open question under which conditions writing through an addr_of! -created pointer is permitted. If the place expr evaluates to is based on a raw pointer, then the result of addr_of! inherits all permissions from that raw pointer. However, if the place is based on a reference, local variable, or static, then until all details are decided, the same rules as for shared references apply: it is UB to write through a pointer created with this operation, except for bytes located inside an UnsafeCell. Use &raw mut (or addr_of_mut) to create a raw pointer that definitely permits mutation.
A slice -- str or [T] -- is not a reference, it's some dynamic range of memory. So IMO the mental model is wrong. (And special casing slices or unsized types for the index operators would introduce inconsistency, complexity, and likely breakage.)
Unfortunately the terminology is not great, as ~everyone uses "slice" for all of [T], &[T], and &mut [T] (or str equivalents). My "favorite" example being the std docs themselves using two meanings within stone's throw.
A dynamically-sized view into a contiguous sequence, [T].
[...]
Slices are a view into a block of memory represented as a pointer and a length.
Not the same thing!
It's just one of those speed-bumps; you have to get used to figuring out which meaning of "slice" was intended.
I haven't seen this mentioned in other comments: types like Box<str> and Rc<[T]> are perfectly valid and used relatively often, and they are not references!
Thanks for the detailed explanation — that makes sense.
I appreciate the insight into why the current slicing behavior exists and the historical constraints behind it.
Very helpful, thank you