Skip to content

str::as_bytes_mut

str methods · Strings

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.

pub const unsafe fn as_bytes_mut(&mut self) -> &mut [u8]

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.

Hello World
HELLO WORLD
"café" is 5 bytes for 4 chars
last char starts at byte 3

See also

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