I'm implementing a type of BSP tree in Rust, and I am wanting to write a function that tells which side of a parent node a node belongs to. This is what I have
enum Side {
L = 0,
R = 1,
}
enum RegionKind {
Split {
subregion: [Option<Rc<Region>>; 2],
},
Client{
window: i32,
},
}
struct Region {
kind: RegionKind,
container: Option<Rc<Region>>,
tags: u8,
}
impl Region {
fn from(&self) -> Option<Side> {
if let RegionKind::Split{subregion,..} = &self.container.as_ref()?.kind {
match &subregion[0] {
None => None,
Some(r) => if eq(Rc::<Region>::as_ptr(&r),self) {
Some(Side::L)
} else {
None
}.or_else(|| { match &subregion[1] {
None => None,
Some(r) => if eq(Rc::<Region>::as_ptr(&r),self) {
Some(Side::R)
} else {
None
}
}})
}
} else {
None
}
}
}
There are currently two things in place that are making this function quite the behemoth.
I need to repeat the check on on subregion[0] and subregion[1]. I would really like to be able to return the
Sideof the index (given that L = 0 and R = 1 like shown in the enum declaration) and be able to combine these into one, hence why I am asking to match over the elements of the array. Something like how you useSome(r) => ...to unbox therout of theSomeI need to check if the
RegionKindisSplit. I can assert that any Region'scontainerwill always haveRegionKind::Splitas theirkind, so I would like to be able to eliminate theif let elseentirely (I would prefer topanic!()in that else block if I must have it as well, but the compiler gets upset about return types so I need to add theNoneat the end anyways, which just ends up getting marked as unreachable)
I figure there's probably some arcane Rust syntax that can make this much more concise and I just don't know it or don't know how to employ it here. This same function can be a single line macro in C.
I would move some stuff to other functions.
Then you can write it concisely.
Note that if you ever find yourself matching an
Option, especially withNone => None, you can almost certainly use anOptionmethod instead, usuallymaporand_then. In this case,?will work since the twoNonevalues are being returned.I believe you don't need to use
Rc::as_ptrsince you are never using the pointer for reading, and theRcis never dropped anyway. You can just derive the pointer straight from the shared reference.What you said about panics isn't true. You can always replace an expression with a panic unless that return is needed for type inference, but since you have an explicit return type, that does not apply here. I've used
unwrapabove as the panic.If you wanted to make it truly one expression, you can do that, but I wouldn't recommend it since it becomes too complex.
You could also replace
[Side::L, Side::R]with(0u8..).map(|n| Side::try_from(n).unwrap())and implementTryFrom<u8> for Side, but I wouldn't bother when you only have two variants. Rust doesn't provide many convenient uses for explicit enum discriminants.