2015-01-24 3 views
8

Я хочу отправить свою структуру через TcpStream. Я мог бы отправить String или u8, но я не могу отправить произвольную структуру. Например:Как преобразовать 'struct' в '& [u8]'?

struct MyStruct { 
    id: u8, 
    data: [u8; 1024], 
} 

let my_struct = MyStruct { id: 0, data: [1; 1024] }; 
let bytes: &[u8] = convert_struct(my_struct); // how?? 
tcp_stream.write(bytes); 

После получения данных, я хочу, чтобы преобразовать &[u8] обратно в MyStruct. Как я могу конвертировать между этими двумя представлениями?

Я знаю, что у Rust есть модуль JSON для сериализации данных, но я не хочу использовать JSON, потому что я хочу отправлять данные как можно быстрее и мал, поэтому я хочу, чтобы не было или очень мало накладных расходов.

ответ

9

(бессовестно украдены из Renato Zannon's comment на подобный вопрос)

Возможно решение, как bincode устраивающих ваш случай? Вот рабочий отрывок:

Cargo.toml

[package] 
name = "foo" 
version = "0.1.0" 
authors = ["An Devloper <[email protected]>"] 

[dependencies] 
bincode = "0.6.1" 
serde_derive = "0.8.23" 

main.rs

extern crate bincode; 
#[macro_use] 
extern crate serde_derive; 

use std::fs::File; 

#[derive(Serialize, Deserialize)] 
struct A { 
    id: i8, 
    key: i16, 
    name: String, 
    values: Vec<String>, 
} 

fn main() { 
    let a = A { 
     id: 42, 
     key: 1337, 
     name: "Hello world".to_string(), 
     values: vec!["alpha".to_string(), "beta".to_string()], 
    }; 

    // Encode to something implementing Write 
    let mut f = File::create("/tmp/output.bin").unwrap(); 
    bincode::serde::serialize_into(&mut f, &a, bincode::SizeLimit::Infinite).unwrap(); 

    // Or just to a buffer 
    let bytes = bincode::serde::serialize(&a, bincode::SizeLimit::Infinite).unwrap(); 
    println!("{:?}", bytes); 
} 

Вы бы тогда быть в состоянии послать байты, куда вы хотите. Я предполагаю, что вы уже знаете о проблемах с наивным отправкой байтов (например, потенциальные проблемы с проверкой подлинности или версией), но я упомянул их на всякий случай^_ ^.

+0

Кажется, это полезно в моем случае !! Спасибо за ответ! – agatana

+0

Стоит отметить, что это не прямое преобразование, в то время как кодирование/декодирование использует двоичный формат, это не просто доступ к памяти структуры (которая может рассматриваться как хорошая и плохая вещь) в зависимости от того, что вы хотите, он выполняет некоторые преобразования. Например, Bincode также выполняет преобразование в endian. – ideasman42

9

Структуру с правильными размерами в виде нулевых копий можно сделать с помощью stdlib и общей функции.

В приведенном ниже примере есть функция многократного использования, называемая any_as_u8_slice, а не convert_struct, так как это утилита для создания роли и создания срезов.

Обратите внимание, что вопрос задает вопрос о преобразовании , этот пример создает только разрешенный для чтения срез, поэтому имеет преимущество в том, что не нужно копировать память.

Heres рабочий пример, основанный на вопрос:

unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] { 
    ::std::slice::from_raw_parts(
     (p as *const T) as *const u8, 
     ::std::mem::size_of::<T>(), 
    ) 
} 

fn main() { 
    struct MyStruct { 
     id: u8, 
     data: [u8; 1024], 
    } 
    let my_struct = MyStruct { id: 0, data: [1; 1024] }; 
    let bytes: &[u8] = unsafe { any_as_u8_slice(&my_struct) }; 
    // tcp_stream.write(bytes); 
    println!("{:?}", bytes); 
} 

Примечание 1) даже несмотря на 3-й партии ящиков может быть лучше в некоторых случаях, это такая примитивная операция, что ее полезно умеют делать в ржавчине.

примечание 2) на момент написания (Rust 1.15), нет поддержки для const функций. Как только это произойдет, можно будет отбросить в массив фиксированного размера вместо среза.

Примечания 3) функции any_as_u8_slice отмечена unsafe, потому что любая обивка байты в struct могут быть инициализирована память (дают неопределенное поведение). Если бы был способ, чтобы входные аргументы использовали только структуры, которые были #[repr(packed)], тогда это могло быть безопасным.

В противном случае функция достаточно безопасна, поскольку она предотвращает переполнение буфера, поскольку выход доступен только для чтения, фиксированное количество байтов и его время жизни привязано к входу.
Если вам нужна версия, которая вернула &mut [u8], это было бы очень опасно, так как модификация могла бы легко создавать непоследовательные/поврежденные данные.

Смежные вопросы