How to iterate over fields of struct

I write some vector graphics to learn rust. I want to iterate over fields of a vector3d struct and do stuff.
I know that it is possible but can't google it correctly. I probably need to use some macro with derive procedure similar to serde.

It's not possible to iterate over the fields of a struct, but you can solve your problem another way, you can convert the vector3d to an array (perhaps with a method), then iterate over that array. Maybe something like this

struct Vector3D {
    x: f32,
    y: f32,
    z: f32,
}

impl Vector3D {
    pub fn as_array(&self) -> [f32; 3] {
        [self.x, self.y, self.z]
    }
}
1 Like

Pretty sure it can be done
https://serde.rs/ implement automatic serialization and deserialization for all fields

Ok, my statement wasn't completely accurate. Let me amend it. It's not possible to iterate over the fields of a struct at runtime, i.e. there's no Vector3D.fields or something similar. serde using procedural macros to iterate over fields at compile time and generates normal Rust code. Using a macro would be overkill here, it would be much simpler to just convert your vector3d to an array.

Note that you still need to annotate your struct with #[derive(Serialize, Deserialize)] to get serde to work. That's because there is no built-in way to iterate over the fields of some arbitrary type.

1 Like

One way to do this is by defining a new struct that holds a reference to the Vector3D, and a counter, and to implement Iterator on that new struct. You can get this struct by calling .iter() on the Vector3D:

fn main() {
    let vec3 = Vector3D {
        x: 3.0,
        y: 7.0,
        z: 22.0,
    };
    for a in vec3.iter() {
        dbg!(a);
    }
}
[src/main.rs:8] a = 3.0
[src/main.rs:8] a = 7.0
[src/main.rs:8] a = 22.0

See the implementation details here: Rust Playground

Personally, I'd prefer converting the Vector3D into an array and iterating over the array like RustyYato suggested. It's simpler and less code to write.

You could implement Index and IndexMut for your Vector3d type:

use std::ops::{Index, IndexMut};

pub struct Vector3d<T> {
    pub x: T,
    pub y: T
    pub z: T
}

impl<T> Index<usize> for Vector3d<T> {
    type Output = T;

    fn index(&self, index: usize) -> &T {
        match index {
            0 => &self.x,
            1 => &self.y,
            2 => &self.z,
            n => panic!("Invalid Vector3d index: {}", n)
        }
    }
}

impl<T> IndexMut<usize> for Vector3d<T> {
    fn index_mut(&mut self, index: usize) -> &mut T {
        match index {
            0 => &mut self.x,
            1 => &mut self.y,
            2 => &mut self.z,
            n => panic!("Invalid Vector3d index: {}", n)
        }
    }
}

Why not define Vector3D as follows:

struct Vector3D([f32; 3]);

No need to convert, then.

1 Like

I believe the new bevy_reflect crate allows you to do this.

Another option, similar to using struct Vector32([f32; 3]) but keeping the field names is to use repr(C), which allows you to still re-use the slice iterators, e.g. by casting with the bytemuck crate. See this playground for example.

2 Likes

Personally, I don't see an advantage of field names like x, y, z over [0], [1], [2] (could just use constants X=0, Y=1, Z=2). However, if it was something descriptive like height, depth, length, I would be convinced to use your method (although constants would still work).

Yes I can slightly change my code to fit my needs, but the purpose of this project is to learn rust, that include the infrastructures and libraries. I can write it the same way that I will write it in c++ but then there is no point for me to write it in rust. I want to learn how to fully utilize rust tools to get the maximum.
I know that #derive(debug) allow me to print every field of the struct which mean there should be macro in the std that allow me to have visitor per field. I just can't find it by myself, at least not very fast

It's a compiler buiilt-in: mod.rs.html -- source
You can accomplish the same thing using procedular macros. For example, the derive_more crate.

If you don't mind doing it by hand, there's DebugStruct and other builders designed to assist manual debug implementations.

1 Like

Rust's main selling point isn't safe transmutation (yet) and most definitely not how easy it is to write procedural macros, that still compile after an update.

Rust is a systems programming language at heart and the crates out there to get things done quickly and safely for you may still be unstable or not exist at all. The situation is constantly improving, but sometimes you'll encounter situations where you just have to do it yourself.

Don't use Rust just to write less code than C++. C++ libraries have had considerably more time to mature and in some areas, Rust will take several more years, perhaps a decade, to catch up with C/C++.

One of Rust's many selling points (when coming from C/C++) is, that the responsibility for memory safety isn't just completely pushed onto the developer and every dependency is a potential security issue. The language takes responsibility for a lot of common memory safety issues and outright prevents them without garbage collection. If you have a mutable reference to something, you can actually be certain, that this is the only reference to it.

As a bonus, neither do you have to deal with header files nor with terribly-designed exceptions of C++, which are spread across the whole standard library.

There are a lot of other things, that I could mention, but that'd take me over an hour. This forum contains several threads that both complain about Rust, as well as threads where people show off what they like about Rust. Outside the forum, you can find a ton of blog posts covering Rust's strength and weaknesses. https://this-week-in-rust.org/ has a section for observation/thoughts and there are 370 weeks full of blog posts you can take a look at, if you desire.