Help converting a `&mut str` into a `&mut [u8]`

Hi everyone,
Im trying to pass a str as a slice of u8 without any success, can someone please point me in the right direction.
Also i cant use String (or any heap allocation, im in a #![no_std] environment) needs to be an &str and the function argument needs to be mutable.

#![allow(unused)]

fn main() {
    let mut mssg = "Heyyy!";

    let mut bytes = unsafe { mssg.as_bytes_mut() };
    do_something(&mut bytes);
}

fn do_something(bytes: &mut [u8]) {
    println!("{:?}", bytes);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `*mssg` as mutable, as it is behind a `&` reference
 --> src/main.rs:6:30
  |
4 |     let mut mssg = "Heyyy!";
  |         -------- help: consider changing this to be a mutable reference: `&mut str`
5 | 
6 |     let mut bytes = unsafe { mssg.as_bytes_mut() };
  |                              ^^^^ `mssg` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

Does this work for you? It uses a b before the string to get a byte array rather than string slice.

fn main() {
    let mut msg: Vec<u8> = b"Heyyy!".to_vec();

    do_something(&mut msg);
}

fn do_something(bytes: &mut [u8]) {
    println!("{:?}", bytes);
}

The type annotation is not necessary, but I added it for clarity.

hi alice thanks for the help, but i cant use any heap allocation.

How about this?

fn main() {
    let mut msg: [u8; 6] = *b"Heyyy!";

    do_something(&mut msg);
}

fn do_something(bytes: &mut [u8]) {
    println!("{:?}", bytes);
}

This uses a dereference to copy the byte array literal into a local variable.

1 Like

it works!, thanks.
Any idea why i cant start from a &str?

Because a string literal (or the byte literal I used) compiles down to a reference into a constant stored in the .rodata section of the binary, so the memory that holds it is immutable. That said, with a &str you could still have copied it to a local variable on the stack like this:

fn main() {
    let mut msg: [u8; 6] = [0; 6];
    msg.copy_from_slice("Heyyy!".as_bytes());

    do_something(&mut msg);
}

But it is easier with a byte array literal because the compiler can verify that the length is right.

3 Likes

Also converting a non-static &mut str into an &mut [u8] would allow writing non-UTF-8 data to the string, which is UB.

let mut string: String = "a".to_string();
let s: &mut str = &mut *string;
let bytes: &mut [u8] = convert_str_to_bytes(s);
bytes[0] = 0xff; // oops, the string is now the sequence of bytes [0xff], which is not valid UTF-8.

https://doc.rust-lang.org/stable/std/primitive.str.html

String slices are always valid UTF-8.

4 Likes