Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper Rust match idiom

I realize the language is at 0.9/0.10 and undergoing breaking changes still (like dropping do this week?), but am curious to know if I'm using sound idiomatics here. I've implemented two versions of the change-making algorithm, one naive and one that looks 'better' in my opinion.

This is for 0.9. Both snippets compile and yield identical results in a couple assert!'s

fn count_change_naive(cents: int, coins: &[int]) -> int {
    if (cents == 0) {
        1
    } else if (cents < 0) {
        0
    } else {
        if coins.iter().len() > 0 {
            let new_amount: int = cents - coins[0];
            let coins_tailed: &[int] = coins.tail();
            count_change_naive(cents, coins_tailed) + count_change_naive(new_amount, coins)
        } else {
            0
        }
    }
}

That's pretty unwieldy IMO. I tried to implement the following:

fn count_change_idiomatic(cents: int, coins: &[int]) -> int {
    match cents {
        n if cents == 0                     => 1,
        n if cents < 0                      => 0,
        n if coins.iter().len() > 0         => {
            let new_amount: int = cents - coins[0];
            let coins_tailed: &[int] = coins.tail();
            count_change_idiomatic(cents, coins_tailed) + count_change_idiomatic(new_amount, coins)
        },
        _                                   => 0
    }
}

I get unused variable warnings on account of the n in the match, but I'm uncertain how to avoid this without an ugly if-else pyramid. Can it be done? Am I missing anything significant in the second implementation?

EDIT: have cleaned up some to meet some of the suggestions, but left intact for comparision

like image 448
ispilledthejava Avatar asked Jun 17 '26 14:06

ispilledthejava


2 Answers

To no get the warnings for the variable you have no use for, you just don't use it:

fn count_change_idiomatic(cents: int, coins: &[int]) -> int {
    match cents {
        _ if cents == 0                     => 1,
        _ if cents < 0                      => 0,
        _ if coins.iter().len() > 0         => {
            let new_amount: int = cents - coins[0];
            let coins_tailed: &[int] = coins.tail();
            count_change_idiomatic(cents, coins_tailed) + count_change_idiomatic(new_amount, coins)
        },
        _                                   => 0
    }
}
like image 144
pepper_chico Avatar answered Jun 19 '26 06:06

pepper_chico


You can use pattern matching on vectors to avoid the length-taking, indexing and tailing:

fn count_change_idiomatic(cents: int, coins: &[int]) -> int {
    match (cents, coins) {
        (0, _)                        => 1,
        _ if cents < 0                => 0,
        (_, [first, .. coins_tailed]) => {
            let new_amount = cents - first;
            count_change_idiomatic(cents, coins_tailed) + 
                count_change_idiomatic(new_amount, coins)
        }
        _                             => 0
    }
}

The .. part of the pattern matches the rest of the vector (in this case, everything but the first element).

like image 22
huon Avatar answered Jun 19 '26 06:06

huon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!