3

I'm trying to implement a parser for a database file format. Conceptually, I'm trying to define a struct like the following:

struct Database {
    schema: Vec<FieldDescriptors>,
    records: Vec<Record>,
}

Record is basically just a Vec<u8>. In order to extract a column from a record, it needs to refer to a schema (to know which bytes to read). I've tried to implement several designs unsuccessfully:

1) Have the Record struct store a reference to either the Database or the schema (stumbled on this answer: Why can't I store a value and a reference to that value in the same struct?, and I understand why it doesn't work).

2) Create different types for the record data (stored in the database struct), and a record that can actually return the appropriate columns(created on demand when needed). A record contains a reference to the data, and a reference to the schema. This worked fine, except that I want to be able to implement the Index trait to access a record. Unfortunately, index must return a reference, so I can't return a new proxy object on demand. (also apparently impossible to do currently Implementing Index trait to return a value that is not a reference)

Other options I've considered: storing a copy of the schema with each record which would be wasteful, or storing the schema in a box and storing a reference to it in each record(seems the least onerous, but still unsatisfying).

Am I missing a good solution to this problem?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Ben Jones
  • 919
  • 1
  • 8
  • 22
  • 1
    Use [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html)? Pass in a reference to a schema only when decoding, not as an attribute of the `Record`? Store the schema separately, don't bundle them? Use owning_ref? – Shepmaster Apr 15 '17 at 01:48
  • RC seems like overkill since the schema lives exactly as long as the database. I'd like to bundle them because I consider the schema to be a internal detail. owning_ref looked like it might be what I want, but I'm a little confused how to use it. I think I want to follow the RcRef for slices example from here, right? https://kimundi.github.io/owning-ref-rs/owning_ref/index.html – Ben Jones Apr 15 '17 at 15:51

1 Answers1

0

You didn't post very much code, but I imagine that you wanted to get the column information from the record using something like:

impl Record {
   fn get_column(&self, index: usize) -> Column {
       // use self.schema to get the column...
   }
}

A completely different design would be to associate this code with the the schema instead, and pass a &Record when you need the schema to do something on it. You can protect any user from having to deal with the schema directly by using a facade, which can be Database itself:

struct Schema {
   field_descriptors: Vec<FieldDescriptors>
}

struct Database {
    schema: Schema,
    records: Vec<Record>,
}

impl Schema {
    fn get_column(&self, record: &Record, index: usize) -> Column {
        // use self.field_descriptors to get the column...
    }
}

impl Database {
    fn get_column(&self, row: usize, col_index: usize) -> Column {
        schema.get_column(&self.records[row], col_index)
    }
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • Won't the user still need to objects in order to get a column out of a record? I was hoping they could do something like `database[i].get_column(2)` . Ideally the user doesn't have to concern themselves with the schema. – Ben Jones Apr 15 '17 at 15:44
  • @BenJones If you access it through the `Database` struct then you can handle that extra argument so the user doesn't have to. I updated the code to include that. – Peter Hall Apr 15 '17 at 17:04