Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I create a mutable slice &mut [u8] from a single byte (u8)?

Tags:

rust

Sometimes I want to read a single byte from a std::io::Reader. If I try to do this:

use std::io::{self, Read};

fn main() {
    let mut byte: u8 = 0;
    io::stdin().read(&mut byte).unwrap();
    println!("byte: {}", byte);
}

I get the following error (which is clear, as byte is not a slice):

error[E0308]: mismatched types
 --> src/main.rs:6:22
  |
6 |     io::stdin().read(&mut byte).unwrap();
  |                      ^^^^^^^^^ expected slice, found u8
  |
  = note: expected type `&mut [u8]`
             found type `&mut u8`

Is there a way I can keep byte as a simple u8 and just take a slice of it, which I can then pass to read()? The obvious way to make this code work is to use an array of length 1:

use std::io::{self, Read};

fn main() {
    let mut byte: [u8; 1] = [0];
    io::stdin().read(&mut byte).unwrap();
    println!("byte: {}", byte[0]);
}

But that's kinda weird feeling throughout the rest of the code, and it would be more natural to use a single u8 rather than a [u8; 1] that I have to index into.

If it's not possible to create a slice from the simple u8 that's okay, but I don't know if it's possible or not and would like to know.

like image 457
Cornstalks Avatar asked Oct 05 '15 01:10

Cornstalks


People also ask

How do you add slices in Rust?

As a result, you cannot use a Rust slice to insert, append or remove elements from the underlying container. Instead, you need either: to use a mutable reference to the container itself, to design a trait and use a mutable reference to said trait.

How do you find the length of a slice in Rust?

We use the len() function to obtain this value.


2 Answers

Rust 1.28+

slice::from_mut is back and it's stable!

use std::{
    io::{self, Read},
    slice,
};

fn main() {
    let mut byte = 0;
    let bytes_read = io::stdin().read(slice::from_mut(&mut byte)).unwrap();
    if bytes_read == 1 {
        println!("read byte: {:?}", byte);
    }
}

Rust 1.0+

But that's kinda weird feeling throughout the rest of the code, and it would be more natural to use a single u8 rather than a [u8; 1] that I have to index into.

Creating an array of length 1 would be the most natural way of doing it:

use std::io::{self, Read};

fn main() {
    let mut bytes = [0];
    let bytes_read = io::stdin().read(&mut bytes).unwrap();
    let valid_bytes = &bytes[..bytes_read];
    println!("read bytes: {:?}", valid_bytes);
}

However, it is possible to unsafely create a slice from a reference to a single value:

use std::io::{self, Read};
use std::slice;

fn mut_ref_slice<T>(x: &mut T) -> &mut [T] {
    // It's important to wrap this in its own function because this is
    // the only way to tell the borrow checker what the resulting slice
    // will refer to. Otherwise you might get mutable aliasing or a
    // dangling pointer which is what Rust is trying to avoid.
    unsafe { slice::from_raw_parts_mut(x, 1) }
}

fn main() {
    let mut byte = 0u8;
    let bytes_read = io::stdin().read(mut_ref_slice(&mut byte)).unwrap();
    if bytes_read != 0 {
        println!("byte: {}", byte);
    }
}

Remember that a slice is basically two things: a pointer to an area of memory and a length. With a slice of length one, you simply need to add a length to a mutable reference and bam! you got yourself a slice.

Earlier versions of Rust had the ref_slice and mut_ref_slice functions. They were removed because their utility was not yet proven (this isn't a common problem), but they were safe to call. The functions were moved to the ref_slice crate, so if you'd like to continue using them, that's one possibility.

like image 131
Shepmaster Avatar answered Nov 05 '22 03:11

Shepmaster


To answer your actual question: no, you can’t do that, and there’s almost never any need to. Even if you couldn’t get an iterable out of a readable, you could just put byte[0] into another variable and use that.

Instead, you can use the Bytes iterator:

let byte: u8 = io::stdin().bytes().next().unwrap();
like image 21
Ry- Avatar answered Nov 05 '22 05:11

Ry-