error[E0308]: mismatched types
2 | let x: &mut str = "qwerty"; // x is declared as `mut`
| -------- ^^^^^^^^ types differ in mutability
| expected due to this
= note: expected mutable reference `&mut str`
found reference `&'static str`
If I really want a &mut str I can totally allocate a String or a Box and call it a day, but I don’t understand why there is this limitation. Is it just because "someone need to do the work"?
I would have expect the following construction to compile. The compiler would have allocated it on the stack (just like any other local variable) some space equal to the size of bytes of the string (it’s known at compile time), then memcpy it from the binary at the beginning of the function (in order to have a different mutable slice for each stack frame of the current function).
If x was instead a static, since there is a single instance of a given static in a program, there is no need to duplicate it with memcpy shenanigans like for local variable. If my memory of my assembly courses is right, this mean that x would be allocated in the bss section, and it should just work (I don’t remember if bss is writable), otherwise we just have to put it in any other writable section of our binary.
To give the context, when solving this year advent of code day 3 part 1, I created a str using include_str! (or with indoc! from the indoc crate for the unit test), I sorted the slice, and then searched fom duplicates using str::partition_dedup. Sorting and finding duplicate can be done with a &mut str (and I don’t see why both of those function couldn’t be const either).
No, because a string literal is not really useful unless it's 'static, and you can't make it 'static if it's stack-allocated. Statics can't be mutable by default, either, because they are global, so they need some sort of synchronization to be protected from (either multi-threaded or single-threaded) race conditions. So you couldn't directly take a mutable reference to a (static) string literal safely, anyway.
But if you are operating on raw bytes, you can't simply sort them! The whole point of str-as-a-type is to guarantee valid UTF-8, which &[u8] can't. Not all arbitrary byte arrays are valid UTF-8. Therefore, there is no guarantee that sorting the bytes of a &str will still be valid UTF-8. Hence, you can't sort the bytes of a &mut str. You have to use a mutable slice for that.
I get your point. But if I don’t control how the str was created (because it was behind a indclude_str! or indoc! macro), then I’m stuck because I have a non mutable str even if it could have been possible to get one. Or is there a way to create one without extra runtime penalty?
Technically yes, practically it's not feasible. Simply because without extra runtime penalty the most you can do is put initial value in the .data segment but they it would only have pre-determined value the first time you would call your function.
Number of bugs this approach have produced in C is so vast that the fact that in Rust that become extremely complicated endeavour can only be considered as a very good thing.
it's possible to do that even in Rust, but that's not trivial — on purpose. Even C ended up with a crazy rules that string literals have type char* but you are no allowed to change them (only compiler wouldn't stop you, you would find out about that when you program would crash and sometimes it would crash not where you [try to] modify the string literal but in some entirely different place).
I didn’t say "no runtime cost", but "no extra runtime cost". I totally expect a memcpy to have a new mutable str on the stack (unless it’s a static it that case it’s not needed and could directly be made mut with all the usual issue and restrictions (unsafe to use) of mutable statics).