Reputation: 824
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?
Upvotes: 8
Views: 7391
Reputation: 15306
Although this is not idiomatic Rust, you can do it using reflection. This is implemented by the bevy_reflect crate.
Your example would be implemented like this.
use bevy_reflect::{GetField, Reflect};
#[derive(Reflect)]
struct A {
foo: String,
bar: String,
baz: String
}
fn main() {
let fields = vec!["foo", "bar", "baz"];
let a = A {
foo: "one".to_string(),
bar: "two".to_string(),
baz: "three".to_string()
};
for field in fields {
let value = a.get_field::<String>(field).unwrap();
println!("{value}");
}
}
This will print:
one
two
three
You can also extract the field names of an arbitrary struct that implements Reflect
.
use bevy_reflect::{Reflect, Struct};
#[derive(Reflect)]
struct A {
foo: String,
bar: String,
baz: String
}
fn main() {
let a = A {
foo: "one".to_string(),
bar: "two".to_string(),
baz: "three".to_string()
};
for (i, value) in a.iter_fields().enumerate() {
let field_name = a.name_at(i).unwrap();
if let Some(value) = value.downcast_ref::<String>() {
println!("{} has value: {}", field_name, *value);
}
}
}
This outputs:
foo has value: one
bar has value: two
baz has value: three
You can even get nested fields using path strings.
See the crate's readme for more information.
Upvotes: 0
Reputation: 111
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 have written a macro that automatically implements such code for any struct. 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"
Upvotes: 11
Reputation: 824
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);
}
}
Upvotes: 3
Reputation: 32197
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
Upvotes: 7