2015-01-24 16 views
8

Desidero inviare la mia struttura tramite TcpStream. Potrei inviare String o u8, ma non posso inviare una struttura arbitraria. Per esempio:Come convertire 'struct' in '& [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); 

Dopo aver ricevuto i dati, voglio convertire &[u8] tornare a MyStruct. Come posso convertire tra queste due rappresentazioni?

So che Rust ha un modulo JSON per la serializzazione dei dati, ma non voglio usare JSON perché voglio inviare i dati il ​​più velocemente e il meno possibile, quindi voglio un no o un overhead molto piccolo.

risposta

9

(spudoratamente rubato da Renato Zannon's comment su una questione analoga)

Forse una soluzione come bincode sarebbe adatto al vostro caso? Ecco un estratto di lavoro:

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); 
} 

Si sarebbe quindi in grado di inviare i byte dove vuoi. Presumo che tu sia già a conoscenza dei problemi con l'invio ingenuo di byte (come potenziali problemi di endianness o versioni), ma li menzionerò solo nel caso^_ ^.

+0

Sembra utile nel mio caso !! Grazie per la risposta! – agatana

+0

Vale la pena notare che questa non è una conversione diretta, mentre la codifica/decodifica utilizza un formato binario, non si tratta semplicemente dell'accesso alla memoria della struttura (che può essere vista sia come una cosa buona che una cattiva) a seconda di ciò che si desidera, sta facendo alcune conversioni. Bincode, ad esempio, fa anche la conversione endian. – ideasman42

9

Una struttura di dimensioni corrette come byte copiati a zero può essere eseguita utilizzando stdlib e una funzione generica.

Nell'esempio riportato di seguito è disponibile una funzione riutilizzabile denominata any_as_u8_slice anziché convert_struct, poiché questa è un'utilità per avvolgere la creazione di cast e slice.

Si noti che la domanda si pone sulla conversione , questo esempio crea una sezione di sola lettura, quindi ha il vantaggio di non dover copiare la memoria.

Ecco un esempio di lavoro in base alla domanda:

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); 
} 

Nota 1) anche se 3rd casse del partito potrebbe essere meglio, in alcuni casi, si tratta di un'operazione così primitiva che il suo utile sapere come fare in Rust.

Nota 2) al momento della scrittura (Rust 1.15), non c'è supporto per le funzioni const. Una volta che c'è, sarà possibile eseguire il cast in una matrice di dimensioni fisse invece di una sezione.

Nota 3) la funzione any_as_u8_slice è contrassegnato unsafe perché qualsiasi byte di riempimento del struct possono essere memoria non inizializzata (dando comportamento indefinito). Se ci fosse un modo per garantire che gli argomenti di input usassero solo le strutture che erano #[repr(packed)], allora potrebbe essere sicuro.

In caso contrario la funzione è abbastanza sicura poiché impedisce l'overflow del buffer poiché l'output è di sola lettura, numero fisso di byte e la sua durata è legata all'input.
Se si desidera una versione che restituisce un &mut [u8], ciò sarebbe molto pericoloso poiché la modifica potrebbe facilmente creare dati incoerenti/corrotti.

Problemi correlati