如何在 Rust 中创建二维数组并动态填入 Structs?
如果每小时只保存一个 struct,建议使用 [[Option<HourData>; 24]; 7],通过赋值写入数据。None 表示该小时尚未收到数据。
如果要保留这句调用:
array[daily_counter][hourly_counter].push(hour_data);
每个小时的位置就需要存放一个 Vec<HourData>,数组类型为 [[Vec<HourData>; 24]; 7],这样同一小时可以保存多条记录。
为什么不能直接调用 push
对于二维数组 [[HourData; 24]; 7]:
array[daily_counter]是当天的[HourData; 24]。array[daily_counter][hourly_counter]是一个HourData。
普通 struct 没有用于追加元素的 push 方法,数组的长度也固定,不能通过 push 扩容。向某个位置写入一个 struct,可以直接赋值;如果需要追加多条记录,就让该位置保存一个 Vec。
每小时一条记录:使用 Option 数组
开始采集时,通常还没有所有小时的数据。可以先将 168 个位置初始化为 None,收到数据后再写入 Some(hour_data)。
示例中的小时数据有四个字段,使用时替换成自己的字段即可:
#[derive(Debug)]
struct HourData {
value1: f64,
value2: f64,
value3: f64,
value4: f64,
}
fn main() {
let mut array: [[Option<HourData>; 24]; 7] =
std::array::from_fn(|_| {
std::array::from_fn(|_| None)
});
let daily_counter: usize = 0;
let hourly_counter: usize = 8;
let hour_data = HourData {
value1: 1.0,
value2: 2.0,
value3: 3.0,
value4: 4.0,
};
array[daily_counter][hourly_counter] = Some(hour_data);
// 借用已保存的数据,避免将它移出数组。
if let Some(data) = &array[daily_counter][hourly_counter] {
println!("{:?}", data);
}
}
外层 std::array::from_fn 创建 7 天,内层创建每天的 24 个位置。每个位置都独立初始化,因此不要求 HourData 实现 Copy 或 Clone。
再次向同一个位置赋值会替换原来的记录。Option 还能区分“尚未采集”和“数值恰好为零”这两种情况。
每小时多条记录:让每个位置保存 Vec
同一小时可能收到多条数据时,可以这样初始化。这里沿用前面的 HourData 定义:
let mut array: [[Vec<HourData>; 24]; 7] =
std::array::from_fn(|_| {
std::array::from_fn(|_| Vec::new())
});
let daily_counter: usize = 0;
let hourly_counter: usize = 8;
let hour_data = HourData {
value1: 1.0,
value2: 2.0,
value3: 3.0,
value4: 4.0,
};
array[daily_counter][hourly_counter].push(hour_data);
// 遍历第 0 天、第 8 小时的全部记录。
for data in &array[daily_counter][hourly_counter] {
println!("{:?}", data);
}
天数和每天的小时数仍然固定为 7 和 24,每个小时保存的记录数量可以动态增长。尚未写入数据的位置是空 Vec。
初始化和填充时的注意事项
不要把容量当作已有元素数量
Vec::with_capacity(24) 只预留容量,创建的 Vec 长度仍然是 0,此时访问 [hourly_counter] 会越界。
如果按天保存 Vec<HourData>,可以按时间顺序追加数据:
days[daily_counter].push(hour_data);
相应元素追加后,才能通过小时索引访问,而且需要自行保证记录顺序与小时对应。如果数据可能缺失或乱序到达,前面的 Option 数组更适合。
重复初始化可能要求 Copy
对于普通的运行时值,数组重复表达式 [value; N] 通常要求元素实现 Copy,所以不能直接用下面的写法创建存放 Vec 的数组:
// 无法编译:Vec<HourData> 没有实现 Copy。
let array = [[Vec::<HourData>::new(); 24]; 7];
std::array::from_fn 会为每个位置调用一次闭包,适合初始化 Vec,也适合初始化包含 String 等非 Copy 字段的 struct。
检查索引范围
这里的索引从零开始:daily_counter 必须小于 7,hourly_counter 必须小于 24。直接索引越界会触发 panic。如果索引来自外部输入,可以通过嵌套的 get_mut 检查位置是否存在,再赋值或调用 push。
备注:内容仅供参考。
