Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: how to eliminate / avoid Option::unwrap calls #44

Open
justahero opened this issue May 25, 2023 · 1 comment
Open

Idea: how to eliminate / avoid Option::unwrap calls #44

justahero opened this issue May 25, 2023 · 1 comment

Comments

@justahero
Copy link

justahero commented May 25, 2023

For people making use of unwrap calls in the code may want to find ways how to eliminate them. Not all unwrap calls are necessary cases for errors, in particular for Option::unwrap.

The following is a short list of examples that avoid the use of unwrap while not panicking. Instead of handling both Some & None via a match statement std Rust offers some alternatives to achieve the same, in particular for iterators.


struct Item(pub Option<&'static str>);

fn main() {
    let input = vec![Item(None), Item(Some("Hello")), Item(Some("World"))];
    let output = input
        .iter()
        .filter(|i| i.0.unwrap().to_lowercase() == "world")
        .collect::<Vec<_>>();
    println!("Found {} items", output.len());
}

This will panic, but what we would like to return here is the number of items that match the (lowercased) world string. The filter line could be written as:

filter(|i| i.0.map_or(false, |i| i.to_lowercase() == "world"))

Using the rowan library assume we would like to return a list of two tokens from the parent node.

let syntax_tokens = t
    .parent()
    .unwrap()
    .siblings_with_tokens(Direction::Next)
    .take(2)
    .collect::<Vec<_>>();

This fails when the given token t does not have a parent. To ignore this case we can rewrite it using the Option::into_iter function to transform the Option into an Iterator. If the Option holds a Some value the iterator executes exactly once, otherwise zero for None .

let syntax_tokens = t
    .parent()
    .into_iter()
    .flat_map(|it| it.siblings_with_tokens(Direction::Next))
    .take(2)
    .collect::<Vec<_>>();

A call to flat_map is necessary here, otherwise it returns an Iterator in a Vec (list in list).


Use filter_map to allow use of ? operator.

struct Number(pub i32);
fn main() {
    let items = [None, Some(Number(1)), Some(Number(2))];
    let sum: i32 = items.into_iter().map(|x| x.unwrap().0).sum();
    println!("Sum is {sum}");
}

rewritten as:

struct Number(pub i32);

fn main() {
    let items = [None, Some(Number(1)), Some(Number(2))];
    let sum: i32 = items.into_iter().filter_map(|x| Some(x?.0)).sum();
    println!("Sum is {sum}");
}

Closures that return an Option or Result allow the ? operator. Method filter_map uses an Option as return type.

@vacuus
Copy link

vacuus commented Sep 9, 2023

Your first example could use .count() and avoid the Vec altogether.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants