Access struct field by variable - rust

I want to iterate over over the fields of a struct and access its respective value for each iteration:
#[derive(Default, Debug)]
struct A {
foo: String,
bar: String,
baz: String
}
fn main() {
let fields = vec!["foo", "bar", "baz"];
let a: A = Default::default();
for field in fields {
let value = a[field] // this doesn't work
}
}
How can I access a field by variable?

Rust doesn't have any way of iterating directly over its fields. You should instead use a collection type such as Vec, array or one of the collections in std::collections if your data semantically represents a collection of some sort.
If you still feel the need to iterate over the fields, perhaps you need to re-consider your approach to your task and see if there isn't a more idiomatic/proper way to accomplish it

By using pattern matching, you can iterate over its fields.
#[derive(Default, Debug)]
struct A {
foo: String,
bar: String,
baz: String
}
impl A {
fn get(&self, field_string: &str) -> Result<&String, String> {
match field_string {
"foo" => Ok(&self.foo),
"bar" => Ok(&self.bar),
"baz" => Ok(&self.baz),
_ => Err(format!("invalid field name to get '{}'", field_string))
}
}
}
fn main() {
let fields = vec!["foo", "bar", "baz"];
let a = A {
foo: "value_of_foo".to_string(),
bar: "value_of_bar".to_string(),
baz: "value_of_baz".to_string()
};
for field in fields {
let value = a.get(field).unwrap();
println!("{:?}", value);
}
}
returns
"value_of_foo"
"value_of_bar"
"value_of_baz"
I am now writing a macro that implements such codes automatically for any struct, although there may be some bugs.
field_accessor (https://github.com/europeanplaice/field_accessor).
Cargo.toml
[dependencies]
field_accessor = "0"
use field_accessor::FieldAccessor;
#[derive(Default, Debug, FieldAccessor)]
struct A {
foo: String,
bar: String,
baz: String
}
fn main() {
let a = A {
foo: "value_of_foo".to_string(),
bar: "value_of_bar".to_string(),
baz: "value_of_baz".to_string()
};
for field in a.getstructinfo().field_names.iter() {
let value = a.get(field).unwrap();
println!("{:?}", value);
}
}
It also returns
"value_of_foo"
"value_of_bar"
"value_of_baz"

Based on the answer of sshashank124 I came to the conclusion that I should use an Hashmap instead of a struct:
fn main() {
let mut B = HashMap::new();
B.insert("foo", 1);
B.insert("bar", 2);
B.insert("baz", 3);
let fields = vec!["foo", "bar", "baz"];
for &field in &fields {
let value = B.get(field);
}
}

Related

Get struct from inside tuple variant [duplicate]

I wish that enums in Rust can be used like Haskell's productive type. I want to
access a field's value directly
assign a field's value directly or make a clone with the changing value.
Directly means that not using too long pattern matching code, but just could access like let a_size = a.size.
In Haskell:
data TypeAB = A {size::Int, name::String} | B {size::Int, switch::Bool} deriving Show
main = do
let a = A 1 "abc"
let b = B 1 True
print (size a) -- could access a field's value directly
print (name a) -- could access a field's value directly
print (switch b) -- could access a field's value directly
let aa = a{size=2} -- could make a clone directly with the changing value
print aa
I tried two styles of Rust enum definition like
Style A:
#[derive(Debug)]
enum EntryType {
A(TypeA),
B(TypeB),
}
#[derive(Debug)]
struct TypeA {
size: u32,
name: String,
}
#[derive(Debug)]
struct TypeB {
size: u32,
switch: bool,
}
fn main() {
let mut ta = TypeA {
size: 3,
name: "TAB".to_string(),
};
println!("{:?}", &ta);
ta.size = 2;
ta.name = "TCD".to_string();
println!("{:?}", &ta);
let mut ea = EntryType::A(TypeA {
size: 1,
name: "abc".to_string(),
});
let mut eb = EntryType::B(TypeB {
size: 1,
switch: true,
});
let vec_ab = vec![&ea, &eb];
println!("{:?}", &ea);
println!("{:?}", &eb);
println!("{:?}", &vec_ab);
// Want to do like `ta.size = 2` for ea
// Want to do like `ta.name = "bcd".to_string()` for ea
// Want to do like `tb.switch = false` for eb
// ????
println!("{:?}", &ea);
println!("{:?}", &eb);
println!("{:?}", &vec_ab);
}
Style B:
#[derive(Debug)]
enum TypeCD {
TypeC { size: u32, name: String },
TypeD { size: u32, switch: bool },
}
fn main() {
// NOTE: Rust requires representative struct name before each constructor
// TODO: Check constructor name can be duplicated
let mut c = TypeCD::TypeC {
size: 1,
name: "abc".to_string(),
};
let mut d = TypeCD::TypeD {
size: 1,
switch: true,
};
let vec_cd = vec![&c, &d];
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
// Can't access a field's value like
// let c_size = c.size
let c_size = c.size; // [ERROR]: No field `size` on `TypeCD`
let c_name = c.name; // [ERROR]: No field `name` on `TypeCD`
let d_switch = d.switch; // [ERROR]: No field `switch` on `TypeCD`
// Can't change a field's value like
// c.size = 2;
// c.name = "cde".to_string();
// d.switch = false;
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
}
I couldn't access/assign values directly in any style. Do I have to implement functions or a trait just to access a field's value? Is there some way of deriving things to help this situation?
What about style C:
#[derive(Debug)]
enum Color {
Green { name: String },
Blue { switch: bool },
}
#[derive(Debug)]
struct Something {
size: u32,
color: Color,
}
fn main() {
let c = Something {
size: 1,
color: Color::Green {
name: "green".to_string(),
},
};
let d = Something {
size: 2,
color: Color::Blue { switch: true },
};
let vec_cd = vec![&c, &d];
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
let _ = c.size;
}
If all variant have something in common, why separate them?
Of course, I need to access not common field too.
This would imply that Rust should define what to do when the actual type at runtime doesn't contain the field you required. So, I don't think Rust would add this one day.
You could do it yourself. It will require some lines of code, but that matches the behavior of your Haskell code. However, I don't think this is the best thing to do. Haskell is Haskell, I think you should code in Rust and not try to code Haskell by using Rust. That a general rule, some feature of Rust come directly from Haskell, but what you want here is very odd in my opinion for Rust code.
#[derive(Debug)]
enum Something {
A { size: u32, name: String },
B { size: u32, switch: bool },
}
impl Something {
fn size(&self) -> u32 {
match self {
Something::A { size, .. } => *size,
Something::B { size, .. } => *size,
}
}
fn name(&self) -> &String {
match self {
Something::A { name, .. } => name,
Something::B { .. } => panic!("Something::B doesn't have name field"),
}
}
fn switch(&self) -> bool {
match self {
Something::A { .. } => panic!("Something::A doesn't have switch field"),
Something::B { switch, .. } => *switch,
}
}
fn new_size(&self, size: u32) -> Something {
match self {
Something::A { name, .. } => Something::A {
size,
name: name.clone(),
},
Something::B { switch, .. } => Something::B {
size,
switch: *switch,
},
}
}
// etc...
}
fn main() {
let a = Something::A {
size: 1,
name: "Rust is not haskell".to_string(),
};
println!("{:?}", a.size());
println!("{:?}", a.name());
let b = Something::B {
size: 1,
switch: true,
};
println!("{:?}", b.switch());
let aa = a.new_size(2);
println!("{:?}", aa);
}
I think there is currently no built-in way of accessing size directly on the enum type. Until then, enum_dispatch or a macro-based solution may help you.

How do I extract information about the type in a derive macro?

I am implementing a derive macro to reduce the amount of boilerplate I have to write for similar types.
I want the macro to operate on structs which have the following format:
#[derive(MyTrait)]
struct SomeStruct {
records: HashMap<Id, Record>
}
Calling the macro should generate an implementation like so:
impl MyTrait for SomeStruct {
fn foo(&self, id: Id) -> Record { ... }
}
So I understand how to generate the code using quote:
#[proc_macro_derive(MyTrait)]
pub fn derive_answer_fn(item: TokenStream) -> TokenStream {
...
let generated = quote!{
impl MyTrait for #struct_name {
fn foo(&self, id: #id_type) -> #record_type { ... }
}
}
...
}
But what is the best way to get #struct_name, #id_type and #record_type from the input token stream?
One way is to use the venial crate to parse the TokenStream.
use proc_macro2;
use quote::quote;
use venial;
#[proc_macro_derive(MyTrait)]
pub fn derive_answer_fn(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Ensure it's deriving for a struct.
let s = match venial::parse_declaration(proc_macro2::TokenStream::from(item)) {
Ok(venial::Declaration::Struct(s)) => s,
Ok(_) => panic!("Can only derive this trait on a struct"),
Err(_) => panic!("Error parsing into valid Rust"),
};
let struct_name = s.name;
// Get the struct's first field.
let fields = s.fields;
let named_fields = match fields {
venial::StructFields::Named(named_fields) => named_fields,
_ => panic!("Expected a named field"),
};
let inners: Vec<(venial::NamedField, proc_macro2::Punct)> = named_fields.fields.inner;
if inners.len() != 1 {
panic!("Expected exactly one named field");
}
// Get the name and type of the first field.
let first_field_name = &inners[0].0.name;
let first_field_type = &inners[0].0.ty;
// Extract Id and Record from the type HashMap<Id, Record>
if first_field_type.tokens.len() != 6 {
panic!("Expected type T<R, S> for first named field");
}
let id = first_field_type.tokens[2].clone();
let record = first_field_type.tokens[4].clone();
// Implement MyTrait.
let generated = quote! {
impl MyTrait for #struct_name {
fn foo(&self, id: #id) -> #record { *self.#first_field_name.get(&id).unwrap() }
}
};
proc_macro::TokenStream::from(generated)
}

Can one specify serde's rename_all rule at runtime?

I have a data model that I would like to be deserialized from "camelCase" to the rust standard "snake_case" when reading from a source, X. But I'd like to leave it in "snake_case" when reading or writing to another source, Y.
For example, the following code,
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Data {
foo_bar: String,
hello_word: String,
}
can only be encoded and decoded in camel case. Even if I manually defined my Serialize and Deserialize implementations, I can't define multiple for the same struct. I could define a second struct that's a copy/paste of the other and then derive but that method would get tedious with multiple large structs. What I would really like to do is specify that rename_all attribute at run-time. But I'm not seeing any way to do that in serde's API.
I think the best way sigh is to just write out one struct Data_ per #[serde(rename_all = ...)], then write one additional struct Data that will be the in-memory representation (which won't be serializable, to remove ambiguity), then implement From in both directions for the Data_s and Data so that they're interconvertible.
Thankfully, we can use a macro so that we only have to specify the fields once. (It is incredibly disgusting nonetheless.)
This playground available here.
use serde::{Deserialize, Serialize}; // 1.0.130
use serde_json; // 1.0.69
macro_rules! interconvertible {
($T:ident <-> $U:ident, $($field_name:ident),*) => {
impl From<$T> for $U {
fn from(t: $T) -> Self {
let $T { $($field_name),* } = t;
Self { $($field_name),* }
}
}
impl From<$U> for $T {
fn from(u: $U) -> Self {
let $U { $($field_name),* } = u;
Self { $($field_name),* }
}
}
};
}
macro_rules! create_data_structs {
($($field_name:ident: $field_type:ty),* $(,)?) => {
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct DataX {
$($field_name: $field_type),*
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
struct DataY {
$($field_name: $field_type),*
}
#[derive(Debug)]
struct Data {
$($field_name: $field_type),*
}
interconvertible!(DataX <-> Data, $($field_name),*);
interconvertible!(DataY <-> Data, $($field_name),*);
}
}
create_data_structs!(foo_bar: String, hello_world: String);
fn main() -> serde_json::Result<()> {
let x1: DataX = serde_json::from_str(r#"{"fooBar": "a", "helloWorld": "b"}"#)?;
let y1: DataY = serde_json::from_str(r#"{"foo_bar": "a", "hello_world": "b"}"#)?;
println!("{:?}, {:?}", x1, y1);
let x2: Data = x1.into();
let y2: Data = y1.into();
println!("{:?}, {:?}", x2, y2);
let x_string = serde_json::to_string(&DataX::from(x2))?;
let y_string = serde_json::to_string(&DataY::from(y2))?;
println!("{:?}, {:?}", x_string, y_string);
Ok(())
}
The output is:
DataX { foo_bar: "a", hello_world: "b" }, DataY { foo_bar: "a", hello_world: "b" }
[Data { foo_bar: "a", hello_world: "b" }, Data { foo_bar: "a", hello_world: "b" }]
"{\"fooBar\":\"a\",\"helloWorld\":\"b\"}", "{\"foo_bar\":\"a\",\"hello_world\":\"b\"}"
Since I'm only every decoding from source X I can utilize the #[serde(alias = ???)] macro. So my above use case would be
#[derive(Serialize, Deserialize)]
struct Data {
#[serde(alias="fooBar")]
foo_bar: String,
#[serde(alias="helloWorld")]
hello_word: String,
}
It's still a little tedious but better than an intermediate struct. It won't work though if I want to decode or encode to different cases.
(I'm not going to mark this as an answer because it's a work-around for my specific use case. If anyone has a more generic solution feel free to answer.)

How to attach possible_values to a struct with structopt?

clap allows you to provide list of accepted values using possible_values like this.
let mode_vals = ["fast", "slow"];
.possible_values(&mode_vals)
How to do this with structopt?
Since structopt 0.3, you can use any method from App and Arg directly:
const MODE_VALS: &[&str] = &["fast", "slow"];
#[derive(StructOpt, Debug)]
struct Opt {
/// The velocity mode
#[structopt(short, long, possible_values(MODE_VALS))]
mode: String,
}
https://github.com/TeXitoi/structopt/blob/master/CHANGELOG.md#raw-attributes-are-removed-198-by-sphynx
clap’s possible_values is exposed as a field option, as shown in this structopt example:
//! How to use `arg_enum!` with `StructOpt`.
use clap::arg_enum;
use structopt::StructOpt;
arg_enum! {
#[derive(Debug)]
enum Baz {
Foo,
Bar,
FooBar
}
}
#[derive(StructOpt, Debug)]
struct Opt {
/// Important argument.
#[structopt(possible_values = &Baz::variants(), case_insensitive = true)]
i: Baz,
}
fn main() {
let opt = Opt::from_args();
println!("{:?}", opt);
}
Notably, this is making use of case_insensitive as well, to allow any case of those variants to be accepted.
If you want more granular control, you could omit case_insensitive and instead implement the variants yourself:
use structopt::StructOpt;
#[derive(Debug)]
enum Baz {
Foo,
Bar,
FooBar
}
impl Baz {
fn variants() -> [&'static str; 3] {
["foo", "bar", "foo-bar"]
}
}
#[derive(StructOpt, Debug)]
struct Opt {
/// Important argument.
#[structopt(possible_values = &Baz::variants())]
i: Baz,
}
fn main() {
let opt = Opt::from_args();
println!("{:?}", opt);
}
Finally, you could also use a string array in the same manner.

How can I create enums with constant values in Rust?

I can do this:
enum MyEnum {
A(i32),
B(i32),
}
but not this:
enum MyEnum {
A(123), // 123 is a constant
B(456), // 456 is a constant
}
I can create the structures for A and B with a single field and then implement that field, but I think there might be an easier way. Is there any?
The best way to answer this is working out why you want constants in an enum: are you associating a value with each variant, or do you want each variant to be that value (like an enum in C or C++)?
For the first case, it probably makes more sense to just leave the enum variants with no data, and make a function:
enum MyEnum {
A,
B,
}
impl MyEnum {
fn value(&self) -> i32 {
match *self {
MyEnum::A => 123,
MyEnum::B => 456,
}
}
}
// call like some_myenum_value.value()
This approach can be applied many times, to associate many separate pieces of information with each variant, e.g. maybe you want a .name() -> &'static str method too. In the future, these functions can even be marked as const functions.
For the second case, you can assign explicit integer tag values, just like C/C++:
enum MyEnum {
A = 123,
B = 456,
}
This can be matched on in all the same ways, but can also be cast to an integer MyEnum::A as i32. (Note that computations like MyEnum::A | MyEnum::B are not automatically legal in Rust: enums have specific values, they're not bit-flags.)
Creating an "enum" with constant values, can be augmented using structs and associated constants.
This is similar to how crates like bitflags works and what it would generate.
Additionally, to prevent direct instantiation of MyEnum you can tag it with #[non_exhaustive].
#[non_exhaustive]
struct MyEnum;
impl MyEnum {
pub const A: i32 = 123;
pub const B: i32 = 456;
}
Then you simply use the "enum" as you otherwise would, by accessing MyEnum::A and MyEnum::B.
People looking at this may stumble upon the introduction and deprecation of FromPrimitive. A possible replacement which might also be useful here is enum_primitive. It allows you to use C-like enums and have them cast between numeric and logical representation:
#[macro_use]
extern crate enum_primitive;
extern crate num;
use num::FromPrimitive;
enum_from_primitive! {
#[derive(Debug, PartialEq)]
enum FooBar {
Foo = 17,
Bar = 42,
Baz,
}
}
fn main() {
assert_eq!(FooBar::from_i32(17), Some(FooBar::Foo));
assert_eq!(FooBar::from_i32(42), Some(FooBar::Bar));
assert_eq!(FooBar::from_i32(43), Some(FooBar::Baz));
assert_eq!(FooBar::from_i32(91), None);
}
The enum-map crate provides the ability to assign a value to the enum record. What is more, you can use this macro with different value types.
use enum_map::{enum_map, Enum}; // 0.6.2
#[derive(Debug, Enum)]
enum Example {
A,
B,
C,
}
fn main() {
let mut map = enum_map! {
Example::A => 1,
Example::B => 2,
Example::C => 3,
};
map[Example::C] = 4;
assert_eq!(map[Example::A], 1);
for (key, &value) in &map {
println!("{:?} has {} as value.", key, value);
}
}
How about this?
enum MyEnum {
A = 123,
B = 456,
}
assert_eq!(MyEnum::A as i32, 123i32);
assert_eq!(MyEnum::B as i32, 456i32);
Just to give another idea.
#[allow(non_snake_case, non_upper_case_globals)]
mod MyEnum {
pub const A: i32 = 123;
pub const B: i32 = 456;
}
Then you can simply use it by accessing MyEnum::A and MyEnum::B or use MyEnum::*.
The advantage of doing this over associated constants is that you can even nest more enums.
#[allow(non_snake_case, non_upper_case_globals)]
mod MyEnum {
pub const A: i32 = 123;
pub const B: i32 = 456;
#[allow(non_snake_case, non_upper_case_globals)]
mod SubEnum {
pub const C: i32 = 789;
}
}
For my project I wrote a macro that automatically generates indexes and sets initial values.
#[macro_export]
macro_rules! cnum {
(#step $_idx:expr,) => {};
(#step $idx:expr, $head:ident, $($tail:ident,)*) => {
pub const $head: usize = $idx;
cnum!(#step $idx + 1usize, $($tail,)*);
};
($name:ident; $($n:ident),* $(,)* $({ $($i:item)* })?) => {
cnum!($name; 0usize; $($n),* $({ $($i)* })?);
};
($name:ident; $start:expr; $($n:ident),* $(,)* $({ $($i:item)* })?) => {
#[macro_use]
#[allow(dead_code, non_snake_case, non_upper_case_globals)]
pub mod $name {
use crate::cnum;
$($($i)*)?
cnum!(#step $start, $($n,)*);
}
};
}
Then you can use it like this,
cnum! { Tokens;
EOF,
WhiteSpace,
Identifier,
{
cnum! { Literal; 100;
Numeric,
String,
True,
False,
Nil,
}
cnum! { Keyword; 200;
For,
If,
Return,
}
}
}
I have created a crate enumeration just for this.
Example using my crate:
use enumeration::prelude::*;
enumerate!(MyEnum(u8; i32)
A = 123
B = 456
);
pub fn main() {
assert_eq!(*MyEnum::A.value(), 123);
assert_eq!(*MyEnum::B.value(), 456);
}

Resources