Question

I want to choose between several options, and if the criteria I'm using for selection happen to be equal, I want one at random (reasonably so, so it's equal-chance each time, rather than arbitrary). This is easily done with nested ifs, but it gets pretty ugly and ends up duplicating code.

A simplified example case, in case that's not clear.

extern crate rand;

fn main() {
    let a = 5;
    let b = 5;

    if a > b {
        println!("Do the thing where a is bigger");
    } else if b < a {
        println!("Do the thing where b is bigger");
    } else { 
        if rand::random::<bool>() {
            // I don't want to duplicate the code here!
            println!("Do the thing where a is bigger");
        } else {
            // Or here!
            println!("Do the thing where b is bigger");
        }
    }

}

(rust playground)

Is there a better way to do this which doesn't require duplicating code? In this simplified example I'm only choosing between two possibilities, but it could actually be a longer list. (What about acting based on the highest of a, b, c, d, where it's possible that there's a two, three, or four-way tie?)

I happen to be learning Rust so if there's any clever language features that'd be cool, but I'm also interested in the general case if possible.

Was it helpful?

Solution

A first general practice is to reduce duplication using functional decomposition. This means decomposing a complex function into simpler, potentially reusable parts. Your code would then duplicate only the function calls:

fn do_a() {
    println!("Do the thing where a is bigger");
}
fn do_b() {
    println!("Do the thing where b is bigger");
}
fn main() {
    ...

    if a > b {
        do_a();
    } else if b < a {
        do_b();
    } else { 
        if rand::random::<bool>() {
            do_a();
        } else {
            do_b();
        }
    }
}

A more effective way is to refactor the code, consolidating the conditional expressions; I don't know Rust, so excuse any language errors, but it would be something like:

fn main() {
    let a = 5;
    let b = 5;

    let agb = if a==b { rand::random::<bool>() } else { a>b };
    if agb {
        do_a();
    } else {
        do_b();
    }
}
Licensed under: CC-BY-SA with attribution
scroll top