Reputation: 50180
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
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
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
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}
};
Upvotes: 3