'Search For Value In An Array'

Posted on Wed 10 April 2019 in rust

I was writing an application that takes a user input and that will eventually update the rc.conf file on my NetBSD machines. When a user adds a service to the rc.conf file, there are a predetermined set of values. I need to make sure the user passed a valid service option. For example a valid service input would be something like, dbus=YES. There are only a few valid service values, [“YES”, “TRUE”, “ON”, “1”, “NO”, “FALSE”, “OFF”, “0”]. Splitting the string on the equal sign yields the string literal of YES. An array of valid response.

In Python a solution would be something like this:

if value in valid_service_list:
    do something

But how is this done with Rust? I posted this question in the Rust User Forum and the community responded; in a most friendly manner. Here are two possible solutions.

Option One:

fn is_valid_value(v: &str) -> bool {
    const SERVICE_VALUES: [&str; 8] = ["YES", "NO", "TRUE", "FALSE", "ON", "OFF", "0", "1"];
    SERVICE_VALUES.iter().any(|x| *x == v)
}

This is a function that takes a reference to a string literal (v) and returns a boolean (bool). The array is defined as a constant and contains eight (8) elements of type string literal. Since an array can be iterated, the iter() method can be used to do just that. But what of this any() method? The Rust documentation has this to say about the any method:

Tests if any element of the iterator matches a predicate. any() takes a closure that returns true or false. It applies this closure to each element of the iterator, and if any of them return true, then so does any(). If they all return false, it returns false. any() is short-circuiting; in other words, it will stop processing as soon as it finds a true, given that no matter what else happens, the result will also be true. An empty iterator returns false.

The value of the interator is passed into a closer (|x|). Remember, the 'x' is a reference to a string literal; therefore, it needs to be dereferenced if we want to compare its value to the string literal passed into the function (v). We use the asterisk (*) to dereference. As soon as there is a match the iter() process stops and a "true" is returned. If there is no match, a false is returned. Note there is no semicolon after SERVICE_VALUES.iter().any(|x| *x == v). For Rust, instead of the return keyword, the semicolon is omitted and that tells the compiler "this is our return value."

Option Two:

fn is_valid_service(value: &str) -> bool {
    match value {
        "YES" | "NO" | "TRUE" | "FALSE" | "ON" | "OFF" | "0" | "1" => true,
        _ => false,
    }
}

Like the solution in option one, a reference to a string literal is passed in and a boolean is returned. This option seems a little more readable. The pipe ( | ) functions as an OR operator. In other words, match on a "YES" or a "NO" or a "TRUE"... etc. If there is a match then return a true, if there is no match return a false. Note the underscore ( _ ). This is a "placeholder" for any other value that does not fit into the match. If this is omitted the Rust compiler will complain that not all the options are covered.

There was another option that was offered, but I did not understand all the "goings on" with that suggestion. You can find the entire thread here. Rust Language Forum

Hope this helps.