简体   繁体   English

有没有办法将结构字段作为数组访问?

[英]Is there a way to access a structs fields as an array?

I'm new to rust and am trying to figure out if this is possible.我是 rust 的新手,我想弄清楚这是否可能。

So sometimes it's cleaner for functions/methods to access data as an array, and sometimes it's cleaner to access data by name所以有时函数/方法以数组的形式访问数据更干净,有时按名称访问数据更干净

in rust I can define something like this:在 rust 中,我可以定义如下内容:

struct Vector3D{
    x : f64,
    y : f64,
    z : f64,
    coordinates : [f64;3]
}

But there needs to only be a single place where each value is stored.但是只需要一个地方来存储每个值。

In Java what I'm asking for is (I believe) not possible and the solution I use is this:在 Java 中,我要求的是(我相信)不可能,我使用的解决方案是:

public class Vector3D {
    public static class Vec3D {
        public static final int ARRAY_SIZE = 3;
        public static final int X = 0;
        public static final int Y = 1;
        public static final int Z = 2;
    }
    private double[] coords;

    public double x(){
        return coords[Vec3D.X];
    }
}

which is a bit kludgy, but allows me to access the values of the fixed size arrays by name.这有点笨拙,但允许我按名称访问固定大小 arrays 的值。

Is there a better way to do something like this in Rust?在 Rust 中有没有更好的方法来做这样的事情?

You can keep coordinates as main storage and provide methods to access each of them by their names.您可以将coordinates作为主要存储,并提供通过名称访问每个坐标的方法。 Moreover, implementing Index / IndexMut traits gives some comfort.此外,实现Index / IndexMut特性会带来一些安慰。

#[derive(Debug)]
struct Vector3D {
    coordinates: [f64; 3],
}

impl Vector3D {
    fn x(&self) -> f64 {
        self.coordinates[0]
    }
    fn y(&self) -> f64 {
        self.coordinates[1]
    }
    fn z(&self) -> f64 {
        self.coordinates[2]
    }
    fn x_mut(&mut self) -> &mut f64 {
        &mut self.coordinates[0]
    }
    fn y_mut(&mut self) -> &mut f64 {
        &mut self.coordinates[1]
    }
    fn z_mut(&mut self) -> &mut f64 {
        &mut self.coordinates[2]
    }
}

impl std::ops::Index<usize> for Vector3D {
    type Output = f64;
    fn index(
        &self,
        idx: usize,
    ) -> &Self::Output {
        &self.coordinates[idx]
    }
}

impl std::ops::IndexMut<usize> for Vector3D {
    fn index_mut(
        &mut self,
        idx: usize,
    ) -> &mut Self::Output {
        &mut self.coordinates[idx]
    }
}

fn main() {
    let mut v = Vector3D {
        coordinates: [1.1, 2.2, 3.3],
    };
    println!("x={}, y={}, z={}", v.x(), v.y(), v.z());
    *v.x_mut() += 10.01;
    *v.y_mut() += 20.02;
    *v.z_mut() += 30.03;
    for idx in 0..3 {
        println!("v[{}]={}", idx, v[idx]);
        v[idx] *= -1.0;
    }
    println!("v={:?}", v);
}
/*
x=1.1, y=2.2, z=3.3
v[0]=11.11
v[1]=22.22
v[2]=33.33
v=Vector3D { coordinates: [-11.11, -22.22, -33.33] }
*/

based on @vallentin's comment I put together this code which I feel directly answers the question:根据@vallentin 的评论,我整理了这段代码,我觉得它直接回答了这个问题:

 use core::ops::Index;
    use core::ops::IndexMut;
    fn main() {
        
        let mut v = Vector3D{x:1.0,y:2.0,z:3.0};
        
        println!("v={:?}", v);
        
        // can read/write by name
        v.x = v.y + 2.0;
        
        println!("v={:?}", v);
        
        // can read/write by directly indexing
        v[2] = v.x - v[1];
        
        println!("v={:?}", v);
    }
    
    #[derive(Debug)]
    struct Vector3D {
        x: f64,
        y: f64,
        z: f64,
    }
    
    impl Index<usize> for Vector3D {
        type Output = f64;
    
        fn index(&self, coordinate: usize) -> &Self::Output {
            
            match coordinate {
                0 => &self.x,
                1 => &self.y,
                2 => &self.z,
                _ => panic!("Invalid Vector3D index: {}", coordinate),
            }
        }
    }
    
    impl IndexMut<usize> for Vector3D {
        fn index_mut(&mut self, coordinate: usize) -> &mut f64 {
            
            match coordinate {
                0 => &mut self.x,
                1 => &mut self.y,
                2 => &mut self.z,
                _ => panic!("Invalid Vector3D index: {}", coordinate),
            }
        }
    }

Output:
v=Vector3D { x: 1.0, y: 2.0, z: 3.0 }
v=Vector3D { x: 4.0, y: 2.0, z: 3.0 }
v=Vector3D { x: 4.0, y: 2.0, z: 2.0 }

basically using the Index & IndexMut traits allows the struct to be directly index for read-only or read-write access基本上使用 Index & IndexMut 特征允许结构直接索引以进行只读或读写访问

Here is another version using Index & IndexMut but without any match statements & with the raw data stored as an array这是另一个使用 Index 和 IndexMut 的版本,但没有任何匹配语句,原始数据存储为数组

use core::ops::Index;
use core::ops::IndexMut;
fn main() {
    
    let mut v = Vector3D {
        coordinates: [1.0, 2.0, 3.0],
    };
    
    println!("v={:?}", v);
    
    // can read/write by name
    v[Vec3D::X] = v[Vec3D::Y] + 2.0;
    
    println!("v={:?}", v);
    
    // can read/write by directly indexing
    v[2] = v[Vec3D::X] - v[1];
    
    println!("v={:?}", v);
}

enum Vec3D{
    X = 0,
    Y,
    Z,
}

#[derive(Debug)]
struct Vector3D {
    coordinates: [f64;3],
}

impl Index<Vec3D> for Vector3D {
    type Output = f64;

    fn index(&self, coordinate: Vec3D) -> &Self::Output {
        &self.coordinates[coordinate as usize]
    }
}

impl IndexMut<Vec3D> for Vector3D {
    fn index_mut(&mut self, coordinate: Vec3D) -> &mut f64 {
        &mut self.coordinates[coordinate as usize]
    }
}

impl Index<usize> for Vector3D {
    type Output = f64;

    fn index(&self, coordinate: usize) -> &Self::Output {
        &self.coordinates[coordinate]
    }
}

impl IndexMut<usize> for Vector3D {
    fn index_mut(&mut self, coordinate: usize) -> &mut f64 {
        &mut self.coordinates[coordinate]
    }
}

Or alternatively the Vector3D struct could be removed entirely或者,可以完全删除 Vector3D 结构

use core::ops::Index;
use core::ops::IndexMut;

// this allows us to use Vector3D in place of [f64;3]
type Vector3D = [f64;3];

fn main() {
    
    // without this v[X] would be v[Vec3D::X] etc
    use crate::Vec3D::*;
    
    let mut v : Vector3D = [1.0, 2.0, 3.0];

    println!("v={:?}", v);
    
    // can read/write by name
    v[X] = v[Y] + 2.0;
    
    println!("v={:?}", v);
    
    // can read/write by directly indexing
    v[2] = v[X] - v[1];
    
    println!("v={:?}", v);
    
}

enum Vec3D{
    X = 0,
    Y,
    Z,
}

impl Index<Vec3D> for Vector3D {
    type Output = f64;

    fn index(&self, coordinate: Vec3D) -> &Self::Output {
        &self[coordinate as usize]
    }
}

impl IndexMut<Vec3D> for Vector3D {
    fn index_mut(&mut self, coordinate: Vec3D) -> &mut f64 {
        &mut self[coordinate as usize]
    }
}

which has the advantage of brevity and compile-time index-out-of-bounds checks but does (for better or worse) implement the name based indexing for all arrays of type [f64;3]它具有简洁和编译时索引越界检查的优点,但确实(无论好坏)为 [f64;3] 类型的所有 arrays 实现基于名称的索引

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM