'How can I convert a buffer of a slice of bytes (&[u8]) to an integer?

I am reading raw data from a file and I want to convert it to an integer:

fn main() {
    let buf: &[u8] = &[0, 0, 0, 1];
    let num = slice_to_i8(buf);
    println!("1 == {}", num);
}

pub fn slice_to_i8(buf: &[u8]) -> i32 {
    unimplemented!("what should I do here?")
}

I would do a cast in C, but what do I do in Rust?



Solution 1:[1]

I'd suggest using the byteorder crate (which also works in a no-std environment):

use byteorder::{BigEndian, ReadBytesExt}; // 1.2.7

fn main() {
    let mut buf: &[u8] = &[0, 0, 0, 1];
    let num = buf.read_u32::<BigEndian>().unwrap();

    assert_eq!(1, num);
}

This handles oddly-sized slices and automatically advances the buffer so you can read multiple values.

As of Rust 1.32, you can also use the from_le_bytes / from_be_bytes / from_ne_bytes inherent methods on integers:

fn main() {
    let buf = [0, 0, 0, 1];
    let num = u32::from_be_bytes(buf);

    assert_eq!(1, num);
}

These methods only handle fixed-length arrays to avoid dealing with the error when not enough data is present. If you have a slice, you will need to convert it into an array.

See also:

Solution 2:[2]

I'd like to give this answer here to commit the following additional details:

  1. A working code snippet which converts slice to integer (two ways to do it).
  2. A working solution in no_std environment.
  3. To keep everything in one place for the people getting here from the search engine.

Without external crates, the following methods are suitable to convert from slices to integer even for no_std build starting from Rust 1.32:

Method 1 (try_into + from_be_bytes)

use core::convert::TryInto;

let src = [1, 2, 3, 4, 5, 6, 7];

// 0x03040506
u32::from_be_bytes(src[2..6].try_into().unwrap());

use core::conver::TryInto is for no_std build. And the way to use the standard crate is the following: use std::convert::TryInto;.

(And about endians it has been already answered, but let me keep it here in one place: from_le_bytes, from_be_bytes, and from_ne_bytes - use them depending on how integer is represented in memory).

Method 2 (clone_from_slice + from_be_bytes)

let src = [1, 2, 3, 4, 5, 6, 7];
let mut dst = [0u8; 4];

dst.clone_from_slice(&src[2..6]);

// 0x03040506
u32::from_be_bytes(dst);

Result

In both cases integer will be equal to 0x03040506.

Solution 3:[3]

This custom serialize_deserialize_u8_i32 library will safely convert back and forth between u8 array and i32 array i.e. the serialise function will take all of your u8 values and pack them into i32 values and the deserialise function will take this library’s custom i32 values and convert them back to the original u8 values that you started with.

This was built for a specific purpose, however it may come in handy for other uses; depending on whether you want/need a fast/custom converter like this.

https://github.com/second-state/serialize_deserialize_u8_i32

Solution 4:[4]

Here’s my implementation (for a different use case) that discards any additional bytes beyond 8 (and therefore doesn’t need to panic if not exact):

pub fn u64_from_slice(slice: &[u8]) -> u64 {
    u64::from_ne_bytes(slice.split_at(8).0.try_into().unwrap())
}

The split_at() method returns a tuple of two slices: one from index 0 until the specified index and the other from the specified index until the end. So by using .0 to access the first member of the tuple returned by .split_at(8), it ensures that only the first 8 bytes are passed to u64::to_ne_bytes(), discarding the leftovers. Then, of course, it calls the try_into method on that .0 tuple member, and .unwrap() since split_at does all the custom panicking for you.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 tpmccallum
Solution 4