str::as_bytes_mut¶
Level: reference · for working programmers
One line: The UTF-8 bytes as a writable &mut [u8] — unsafe, because you are being handed the bytes that the type's validity promise is made about.
Stable since 1.20.0. unsafe — the caller carries the invariant described below. Usable in a const context.
The safety contract is one sentence: the slice must still hold valid UTF-8 when the borrow ends. Between those two moments the compiler is not watching, so it is on the caller to keep every multi-byte character whole. Break it and the result is undefined behaviour rather than mojibake — subsequent calls like chars are entitled to skip the validity check entirely.
Two rules make it manageable in practice. Replacing an ASCII byte with another ASCII byte is always sound, because each is a complete character. Anything else — inserting, deleting, or writing a byte ≥ 0x80 — is only sound if you have worked out the full encoding by hand.
[u8]::make_ascii_uppercase, str::make_ascii_uppercase and String::retain cover almost every real use safely. Reach for this when you have a measured reason.
Example¶
str_as_bytes_mut.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.
fn main() {
let mut owned = String::from("hello world");
// Sound: ASCII for ASCII, same byte count, still valid UTF-8.
unsafe {
let bytes = owned.as_mut_str().as_bytes_mut();
bytes[0] = b'H';
bytes[6] = b'W';
}
println!("{owned}");
// Sound for the same reason, and safe to write.
let mut safe = String::from("hello world");
safe.as_mut_str().make_ascii_uppercase();
println!("{safe}");
// The boundary that makes the unsafe version unsafe.
let mixed = "café";
println!("{:?} is {} bytes for {} chars", mixed, mixed.len(), mixed.chars().count());
println!("last char starts at byte {}", mixed.char_indices().last().unwrap().0);
}
Verified output of str_as_bytes_mut.rs — regenerated by tools/run_examples.py, never hand-typed.
See also¶
str::as_bytes— the read-only version, which is safestr::as_mut_ptr— the same permission as a raw pointerstr::make_ascii_lowercase— the safe in-place editString::as_mut_vec— the owned equivalent, with the same contract
str::as_bytes_mut in the standard library ↗
Po polsku¶
Cały kontrakt tej metody mieści się w jednym zdaniu: gdy pożyczenie się kończy, w wycinku musi znowu być poprawny UTF-8 — pomiędzy tymi chwilami kompilator nikomu nie patrzy na ręce, więc to wywołujący odpowiada za to, by żaden znak wielobajtowy nie został przecięty. Warto tu porzucić odruch z innych języków: złamanie kontraktu nie kończy się krzaczkami, tylko niezdefiniowanym zachowaniem (undefined behaviour), bo późniejsze chars() ma pełne prawo w ogóle nie sprawdzać poprawności. Bezpieczna jest właściwie tylko podmiana bajtu ASCII na inny bajt ASCII, a to oznacza, że dla polskiego tekstu ta metoda niemal nie ma zastosowania — ł zajmuje dwa bajty, l jeden, więc taka „drobna poprawka w miejscu” zmienia długość i natychmiast wypada z kontraktu. Do prawdziwych zadań są make_ascii_uppercase i String::retain; as_bytes_mut zostaw na sytuacje, w których masz zmierzony powód.
Szukaj po polsku: niezdefiniowane zachowanie · niezmiennik typu · rust as_bytes_mut safety · rust unsafe utf-8 invariant