I'd like to create a struct that has a byte array, where a particular instance may have different sizes based on the creation of the struct known at compile time.
I've created a contrived example using a struct that has a byte representation of a floating point number with a separate type field. The working implementation below:
#![feature(float_to_from_bytes)]
#[derive(Debug)]
enum TypeMarker {
NUMBER = 0x00, // f64
// BOOLEAN: u8 = 0x01, // bool
// STRING: u8 = 0x02, // UTF-8 string
}
#[derive(Debug)]
struct Value {
t: TypeMarker,
bytes: [u8; 8]
}
impl From<f64> for Value {
fn from(v: f64) -> Self {
Value {
t: TypeMarker::NUMBER,
bytes: v.to_be_bytes()
}
}
}
fn main() {
let num = 4.0;
println!("num = {:?}", num);
let v1 = Value::from(4.0);
println!("Value::from(4.0) = {:?}", v1);
let v2:Value = num.into();
println!("num.into() = {:?}", v2);
}
This working example (see also repo on github) uses rust nightly.
Running the example...
cargo +nightly run --example into
produces the result I expect:
num = 4.0
Value::from(4.0) = Value { t: NUMBER, bytes: [64, 16, 0, 0, 0, 0, 0, 0] }
num.into() = Value { t: NUMBER, bytes: [64, 16, 0, 0, 0, 0, 0, 0] }
However, what I want to do is to support various types of numbers where the size is known at compile time. To illustrate this question, the example below adds impl From<i32> (which is 4 bytes long):
#![feature(float_to_from_bytes)]
#[derive(Debug)]
enum TypeMarker {
NUMBER = 0x00, // f64
// BOOLEAN: u8 = 0x01, // bool
// STRING: u8 = 0x02, // UTF-8 string
}
#[derive(Debug)]
struct Value {
t: TypeMarker,
bytes: [u8; 8]
}
impl From<f64> for Value {
fn from(v: f64) -> Self {
Value {
t: TypeMarker::NUMBER,
bytes: v.to_be_bytes()
}
}
}
impl From<i32> for Value {
fn from(v: i32) -> Self {
Value {
t: TypeMarker::NUMBER,
bytes: v.to_be_bytes()
}
}
}
fn main() {
let num = 4.0;
println!("num = {:?}", num);
let v1 = Value::from(4.0);
println!("Value::from(4.0) = {:?}", v1);
let v2:Value = num.into();
println!("num.into() = {:?}", v2);
}
this produces the following error
error[E0308]: mismatched types
--> examples/into.rs:33:20
|
33 | bytes: v.to_be_bytes()
| ^^^^^^^^^^^^^^^ expected an array with a fixed size of 8 elements, found one with 4 elements
|
= note: expected type `[u8; 8]`
found type `[u8; 4]`
I would like to declare Value struct so that it can be created with variable sized arrays of bytes (where the size is known at compile time).
I've tried:
struct Value {
t: TypeMarker,
bytes: [u8; usize]
}
error[E0423]: expected value, found builtin type `usize`
--> examples/into.rs:17:17
|
17 | bytes: [u8; usize]
| ^^^^^ not a value
error[E0277]: arrays only have std trait implementations for lengths 0..=32
--> examples/into.rs:17:5
|
17 | bytes: [u8; usize]
| ^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[u8; _]`
|
= note: required because of the requirements on the impl of `std::fmt::Debug` for `[u8; _]`
= note: required because of the requirements on the impl of `std::fmt::Debug` for `&[u8; _]`
= note: required for the cast to the object type `dyn std::fmt::Debug`
So then I tried:
struct Value {
t: TypeMarker,
bytes: [u8; _]
}
that didn't work either:
error: expected expression, found reserved identifier `_`
--> examples/into.rs:17:17
|
17 | bytes: [u8; _]
| ^ expected expression
error: aborting due to previous error
This seems like it should be possible and I think I've read the syntax for this once, but I've re-read many sections of the Rust book and looked at dozens of other posts and can't quite seem to figure out the syntax.
Question: How to change bytes declaration to fix the example above that illustrates the error? And, if that is not supported or not idiomatic, what approach would work?
Vec. But not all hopes are lost; RFC 2000 constant generics is to address this exact limitation.