0

I have a structure of heterogenous data:

struct Data {
    a: i16,  // index "0"
    b: i32,  // index "1"
    c: u8,   // index "2"
}

Is there a good way to create an "access table" (can be done at compile time with a build tool) which allows me to access references to the underlying data by index rather than field name? The data will be a nested struct of non-homogenous data (u8, i8, u16, i16, etc). The "indexs" are basically the number corresponding to each item of the nested struct were flattened.

The application is for schema-based communication. We have a run-time database consisting of a static hierarchical struct and a lightweight communication protocol for reading and writing to the run-time database using power-line communication. Due to bandwidth limitations, we use a schema system where we access the structure by index rather than name.

#################### UPDATE (My Solution) ########################

Thanks to those who provided the links below. They helped give me ideas but were not perfect fits to my problem. A little bit more background first, this is a rather large hierarchical struct with around 1000 "items". Each "item" is going to be primitive type of u8, i8, u16, i16, u32, i32, u64, i64, or char. We treat this struct as like a run-time database for shared resources between embedded applications running on an ARM M4.

We have a protocol specification for reading/writing 1 or several values of this runtime database over UART and over our own Powerline Communication modem. For bandwidth reasons, we create a schema system similar to protobuf for accessing individual items by index rather than name. In our C implementation, we have a Python library that generates an "Access Table" at compile time. This "Access Table" contains fixed size array of structs, where each struct contains access info including pointer address and number the size of the item. We then have an access function that is able to fill a buffer with the raw contents of the data using a memcopy. It is important that the access table use pointers, as the application has access to this runtime database and is making changes to the values as the application runs.

Since we only have 9 different variants for the "items" the solution I came up with was to simply encapsulate each item in an Enum. Here is a syntactically correct (tested) implementation. I can then use pattern matching to fill my buffer in the protocol code. I am curious what more experienced Rustaceans think of this solution.

// This is the runtime database struct
#[derive(Debug)]
struct Data {
    a: i32,
    b: u8
}

#[derive(Debug)]
enum Item<'a> {
    U8(&'a u8),
    I8(&'a i8),
    U16(&'a u16),
    I16(&'a i16),
    U32(&'a u32),
    I32(&'a i32),
    U64(&'a u64),
    I64(&'a i64)
}


fn main() {
    // initialize the data in the runtime database
    let data = Data{a:2001, b:42};
    
    // this "access table" generated by Python script at compile time
    // allows indexed access to the data by reference
    let table: [Item; 2] = [Item::I32(&data.a), 
                            Item::U8(&data.b)];
    
    let item:&Item = &table[1];
    
    println!("Struct data => {:?}", data);
    println!("The second value of the struct is {:?}", item);
}
Darko
  • 589
  • 1
  • 7
  • 18
  • The code presented in the post is not syntactically valid Rust. – Shepmaster Sep 01 '21 at 00:35
  • It's hard to answer your question because it doesn't include a [MRE]. We can't tell what crates (and their versions), types, traits, fields, etc. are present in the code. It would make it easier for us to help you if you try to reproduce your error on the [Rust Playground](https://play.rust-lang.org) if possible, otherwise in a brand new Cargo project, then [edit] your question to include the additional info. There are [Rust-specific MRE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. Thanks! – Shepmaster Sep 01 '21 at 00:35
  • See also [Is there a way to perform an index access to an instance of a struct?](https://stackoverflow.com/q/28126735/155423) – Shepmaster Sep 01 '21 at 00:37
  • Your question might be answered by the answers of [Is there a way to access a structs fields as an array?](https://stackoverflow.com/q/66439721/155423); [Is it possible to access elements of a struct using pointers in Rust?](https://stackoverflow.com/q/64307184/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Sep 01 '21 at 00:37
  • Does this answer your question? [Is there a way to access a structs fields as an array?](https://stackoverflow.com/questions/66439721/is-there-a-way-to-access-a-structs-fields-as-an-array) – Jmb Sep 01 '21 at 06:58

2 Answers2

0

My best approach so far is to encapsulate with an Enum.

enum Item<'a> {
    I16(&'a i16),
    U32(&'a u32),
    U8(&'a u8)
}

let access_table: [Item; 3] = [Item::I16(&data.a), 
                               Item::U32(&data.b),
                               Item::U8(&data.c)];
Darko
  • 589
  • 1
  • 7
  • 18
0

If you can live with accessing the items in your struct through serialization and deserialization (these are supported by all your Item types), you could use a trait like this one from miniconf.

You can access your items by their names (a, b, c in your example) or by indices.

This is it using your code:

use miniconf::{JsonCoreSlash, Miniconf};

// This is the runtime database struct
#[derive(Debug, Miniconf)]
struct Data {
    a: i16,  // index "0"
    b: i32,  // index "1"
    c: u8,   // index "2"
}

fn main() {
    // initialize the data in the runtime database
    let data = Data{a:2001, b:42, c: 1};
    
    let mut buf = [0; 64];

    // If you want to look up your item by indices:
    let len = data.get_json_by_index(&[1], &mut buf).unwrap();
    // If you want to look up by path:
    // let len = data.get_json("/b", &mut buf).unwrap();

    let item = core::str::from_utf8(&buf[..len]).unwrap();

    println!("Struct data => {:?}", data);
    println!("The second value of the struct is {:?}", item);
}