Преобразование примитивов числа (i32, f64 и т.д.) В байтовые представления

Я пишу библиотеку, которая кодирует/декодирует данные в/из двоичного формата. Часть формата цифры, которые я использую Руст родные примитивные типы для (как i8, i64, f32 и т.д.).

Есть простой, встроенный способ, чтобы преобразовать эти типы данных в/из двоичного файла, то есть преобразовать f64/f32/i64/др. в Vec<u8>? Аналогично, есть ли способ конвертировать 4 u8 (u8 в Vec<u8>) в f32?

Ответ 1

К сожалению, в настоящее время в Rust отсутствует безопасная встроенная поддержка чтения/записи примитивов из/в байтовый массив. Есть несколько библиотек сообщества для этого, однако, порядком байтов будучи наиболее часто используемых одно:

extern crate byteorder;

use byteorder::{LittleEndian, WriteBytesExt};
use std::mem;

fn main() {
    let i: i64 = 12345;
    let mut bs = [0u8; mem::size_of::<i64>()];
    bs.as_mut()
        .write_i64::<LittleEndian>(i)
        .expect("Unable to write");

    for i in &bs {
        println!("{:X}", i);
    }
}

Конечно, вы всегда можете использовать сырые указатели. Например, вы можете превратить *const i64 в *const i8 а затем преобразовать его в соответствующий фрагмент байта &[u8]. Тем не менее, это легко сделать неправильно, unsafe и зависит от платформы из-за порядка байтов, поэтому его следует использовать только в качестве последнего средства:

use std::{mem, slice};

fn main() {
    let i: i64 = 12345;
    let ip: *const i64 = &i;
    let bp: *const u8 = ip as *const _;
    let bs: &[u8] = unsafe { slice::from_raw_parts(bp, mem::size_of::<i64>()) };

    for i in bs {
        println!("{:X}", i);
    }
}

Ответ 2

Начиная с Rust 1.32 вы можете использовать {to,from}_{ne,le,be}_bytes для целочисленных типов.

let begin = 1234_i32;
let bytes = begin.to_ne_bytes();
let and_back = i32::from_ne_bytes(bytes);

Для плавающей запятой вам все равно придется полагаться на предыдущие методы.

Ответ 3

Можно использовать std::mem::transmute, хотя это unsafe:

fn main() {
    let var1 = 12345678_i64;
    let raw_bytes: [i8; 8] = unsafe { std::mem::transmute(var1) };
    for byte in &raw_bytes {
        println!("{}", byte);
    }
}

Примечание: пожалуйста, убедитесь, что размер двух переменных абсолютно одинаков.

Ответ 4

Строительство от Николас Ришель ответ.

Rust 1.32 содержит: {to,from}_{ne,le,be}_bytes, to_bits и from_bits.

Преобразование целого числа в байты и обратно:

let x = 65535_i32;
let x_bytes = x.to_ne_bytes();                  // x_bytes = [255, 255, 0, 0] or [0, 0, 255, 255]
let original_x = i32::from_ne_bytes(x_bytes);   // original_x = 65535 = x

Преобразование числа с плавающей запятой в байты и обратно:

let y = 255.255_f64;
let y_bytes = y.to_bits().to_ne_bytes();
let original_y = f64::from_bits(u64::from_ne_bytes(y_bytes)); // original_y = 255.255 = y

Согласно документации Rust from_bits могут возникать проблемы с переносимостью.

Ржавчина ночная:

Rust Nightly добавляет {to,from}_{ne,le,be}_bytes для типов с плавающей запятой: проблема.

Преобразование числа с плавающей запятой в байты и обратно (по ночам):

#![feature(float_to_from_bytes)]

let y = 255.255_f64;
let y_bytes = y.to_ne_bytes();
let original_y = f64::from_ne_bytes(y_bytes); // original_y = 255.255 = y

Ответ 5

Если ваша цель состоит в том, чтобы печатать байты или иметь их в представлении str, просто используйте обозначение :b в скобках формата

fn main() {
    println!("This is the binary of int {:b}", 4 as i32);
}

Отпечатает

This is the binary of int 100