pm100
pm100

Reputation: 50180

are succinct struct initializers possible

say I have

struct S1{
  name:&'s str,
  start : u8,
  end : u8,
}

I would like to make an array of them

in C and c++ I can go (a v common pattern )

  S1 arr[] ={
     {"foo",1,2},
     {"bar",4,5},
  }

Seems like this must be in rust

  let arr = [
     S1{name:"foo", start:1, end: 2},
     S1{name:"bar", start:4, end: 5},
    ]

The problem here is a) its a lot of typing b) the information in the table is drowned out by the repeated struct and field names.

I have worked out that I can use tuples

let arr = [("foo",1,2),("bar",3,4)]

but I loose the ability to refer to the fields by name.

Did I miss something? Maybe a helper macro? (3 days into writing rust so apologies if FAQ or plain dumb)

Upvotes: 1

Views: 131

Answers (3)

pm100
pm100

Reputation: 50180

This is what I ended up doing.

using the macro suggested by @EvilTak and mixing in https://docs.rs/phf/0.8.0/phf/

I got this

macro_rules! expand {
    ( $( ($name:expr, $start:expr, $end:expr) ),* ) => {
        [ $( RegDef { name: $name, start: $start, end: $end } ),* ]
    }
}

#[derive(Debug)]
struct RegDef{
    name:&'static str,
    start: usize,
    end: usize
}

static REGDEF_MAP: phf::Map<&'static str, &[RegDef]> = phf_map!{
    "rkcs" => &expand![
        ("err", 15, 16),
        ("he", 14, 15),
        ("scp", 13, 14),
        ("inhib", 11, 12),
        ("fmt", 10, 11),
        ("sse", 8, 9),
        ("rdy", 7, 8),
        ("ide", 6, 7),
        ("mex", 4, 6),
        ("func", 1, 4),
        ("go", 0, 1)
    ],
    "rker" => &expand![
        ("dre", 15, 16),
        ("ovr", 14, 15),
        ("wlo", 13, 14),
        ("ske", 12, 13),
        ("pge", 11, 12),
        ("nxm", 10, 11),
        ("dlt", 9, 10),
        ("tme", 8, 9),
        ("nxd", 7, 8),
        ("nxc", 6, 7),
        ("nxs", 5, 6),
        ("cse", 1, 2),
        ("wce", 0, 1)
    ],
    "rkds" => &expand![
        ("drid", 13, 16),
        ("pwr", 12, 13),
        ("rk05", 11, 12),
        ("dru", 10, 11),
        ("seeki", 9, 10),
        ("scok", 8, 9),
        ("rdy", 7, 8),
        ("ardy", 6, 7),
        ("wp", 5, 6),
        ("scsa", 4, 5),
        ("sc", 0, 4)
    ],
    "rkda" => &expand![
        ("drv", 13, 16),
        ("cyl", 5,13),
        ("sur", 4, 5),
        ("sec", 0, 4)
    ],

};

Upvotes: 1

Michael Anderson
Michael Anderson

Reputation: 73480

I agree it can be unwieldy to write out the whole list. I find that I only have that kind of situation in tests. In that case what I do is write a tiny local function above the list and use that. If I end up reusing it in a few tests, I might move it to the test module scope. Either way, because we've controlled the scope of the function using a very short name doesn't feel so bad.

pub fn main() {
    fn s(name: &str, start: u8, end: u8) -> S1 {
        S1 { name, start, end }
    }
    let arr = [s("foo", 1, 2), s("bar", 4, 5)];
    println!("arr={:?}", arr2)
}

You can see a complete working example in the playground.

Upvotes: 1

EvilTak
EvilTak

Reputation: 7579

You can choose to use a constructor function instead, which will be equivalent to the tuple version but with an extra prefix due to the function calls:

let arr = [S1::new("foo",1,2), S1::new("bar",3,4)]

If you would like to emulate the tuple array / C++ braced and have nothing besides the initial values inside the array, you can use a macro to expand a list of argument tuples/groups into a constructor call:

let arr = expand![
    ("foo", 1, 2), 
    ("bar", 3, 4), 
    ("baz", 5, 6), 
    ("qux", 7, 8)
];

The macro expand! is declared as follows:

macro_rules! expand {
    ( $( ($name:expr, $start:expr, $end:expr) ),* ) => {
        [ $( S1 { name: $name, start: $start, end: $end } ),* ]
    }
}

If you would like to use braces instead of parentheses to group the arguments, simply replace the parentheses enclosing $name:expr, $start:expr, $end:expr in the matcher with braces. Note that you can call a macro with braces, parentheses or rectangular brackets, so you could could make it look exactly like the C++ version, barring the macro call:

let arr = expand! {
    {"foo", 1, 2}, 
    {"bar", 3, 4}, 
    {"baz", 5, 6}, 
    {"qux", 7, 8}
};

Playground

Upvotes: 3

Related Questions