Cast complex data to array of u8

I want to cast [f64; 3] to [u8; 24] for data I send using udp socket.
Is there a best way to do it? Not to cast every float to bytes.

For example, Ruby provides pack/unpack functions. It helps to combine strings and numerics in one data package.

I think the general consensus for serialization is to use the Rust project serde. Read up on it and see if it will fit your needs.

I'll go hide right after I suggest ... std::mem::transmute? :slight_smile:

I would say use byteorder - this will let you read/write f32 and f64 to a Read/Write socket, and will work safely communicating between little endian systems and big endian ones.

std::mem::transmute is usable for this too, but it will only work correctly for systems with the same byte order. serde can be nice too if you want more structured messages, maybe with bincode, but it is definitely a 'higher level' solution.

1 Like

Repeating what others have already said here,

  • The quck and dirty way to cast to and from plain bytes is mem::transmute. It's unsafe, not very nice and it won't work if you're sending the data to a host with different endianness -- that is for floats; more complex objects can even have different layout depending on whether you compile a debug or a release build.
  • A nicer way is to use the byteorder crate. It's higher-level and safe. Still, it's an ad-hoc solution.
  • The proper way is to use Serde, Rust's serialization and deserialization framework. It provides general support for serializing/deserializing [almost] any kinds of objects, including float arrays, to many formats. It is nice and safe and full-featured and super fast.

For the format, you can use, for example, bincode. Here's the code that converts an [f64; 3] to bincode using Serde and sends it to a UDP socket:

#[macro_use]
extern crate bincode;

let data: [f64; 3] = [1.2, 3.4, 5.6];
let serialized = bincode::serialize(&data, bincode::SizeLimit::Infinite)
                    .expect("couldn't serialize data");
let socket = std::net::UdpSocket::bind("127.0.0.1:3542")
                    .expect("couldn't bind to address");
socket.send_to(&serialized, "127.0.0.1:2267")
                    .expect("couldn't send data");

Notice how bincode::serialize call is in fact simpler and more convenient than unsafe { std::mem::transmute::<&[u8; 24]>(&data) } (or the version without &s).

1 Like

Wow! Glad to see community in the action.
Thank you all, I will take a look.

I'd be remiss if I didn't plug: https://github.com/m4b/scroll whichs supports endian-aware reading of the numeric types, and can read arbitrary types out of the data as long as they implement trait TryFromCtx, as well as several other features that I think are cool.

I choose bincode. Satisfied all my needs, easy to pick up, easy to use.