Hello all,
I'm writing a program that controls street intersection traffic lights, as a learning exercise. One of the goals is to increase traffic safety by ensuring that potentially dangerous lights combinations are impossible (on the program level). For that purpose, I want to use enums.
As an example, let's say there is a street that goes in the north-south direction, and the intersection has two traffic lights - one at the upper part (top) that controls traffic from north to south, and one at the lower part (bottom) that controls traffic from south to north. Each traffic light has a main section (for the straight direction) and a left arrow.
For the green light, the forbidden combinations would be "Top Main + Bottom Arrow" and "Bottom Main + Top Arrow". All other combinations are allowed.
I'm using a state variable that determines "right of way" - it contains a collection of traffic light sections that should turn on the green light at a given moment. The variants of the enum type provide the allowed section combinations, and only them.
I expected to have something along the lines of the following code (it doesn't compile, of course):
enum Street {
Meridian,
Parallel,
}
enum LightType {
MainTop,
MainBottom,
ArrowTop,
ArrowBottom,
None,
}
enum AllowedLightCombo {
MainBoth(MainTop, MainBottom),
ArrowBoth(ArrowTop, ArrowBottom),
MainArrowTop(MainTop, ArrowTop),
MainArrowBottom(MainBottom, ArrowBottom),
MainOnlyTop(MainTop),
MainOnlyBottom(MainBottom),
ArrowOnlyTop(ArrowTop),
ArrowOnlyBottom(ArrowBottom),
None,
}
struct StreetLight {
street: Street,
light: AllowedLightCombo,
}
let r_o_w = StreetLight {
street: Street::Meridian,
light: AllowedLightCombo::MainBoth,
};
Eventually, I was able to get the modified code to run, but it looks quite ugly:
#![allow(unused)]
fn main() {
#[derive(Debug)]
enum Street {
Meridian,
Parallel,
}
#[derive(Debug)]
enum MainTop {
Dummy,
}
#[derive(Debug)]
enum MainBottom {
Dummy,
}
#[derive(Debug)]
enum ArrowTop {
Dummy,
}
#[derive(Debug)]
enum ArrowBottom {
Dummy,
}
#[derive(Debug)]
enum LightType {
MainBoth(MainTop, MainBottom),
ArrowBoth(ArrowTop, ArrowBottom),
MainArrowTop(MainTop, ArrowTop),
MainArrowBottom(MainBottom, ArrowBottom),
MainOnlyTop(MainTop),
MainOnlyBottom(MainBottom),
ArrowOnlyTop(ArrowTop),
ArrowOnlyBottom(ArrowBottom),
None,
}
#[derive(Debug)]
struct StreetLight {
street: Street,
light: LightType,
}
let r_o_w_1 = StreetLight {
street: Street::Meridian,
light: LightType::MainBoth(MainTop::Dummy, MainBottom::Dummy),
};
let r_o_w_2 = StreetLight {
street: Street::Meridian,
light: LightType::ArrowBoth(ArrowTop::Dummy, ArrowBottom::Dummy),
};
println!("{:?}", r_o_w_1);
println!("{:?}", r_o_w_2);
}
$ cargo run
Compiling light v0.1.0 (rust/projects/light)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
Running `rust/projects/light/target/debug/light`
StreetLight { street: Meridian, light: MainBoth(Dummy, Dummy) }
StreetLight { street: Meridian, light: ArrowBoth(Dummy, Dummy) }
Are there better ways to achieve that original goal ? What would be the good/recommended practice for cases like this ?
Thank you for your help.