Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Rust need the `if let` syntax?

Tags:

option

rust

Coming from other functional languages (and being a Rust newbie), I'm a bit surprised by the motivation of Rust's if let syntax. The RFC mentions that without if let, the "idiomatic solution today for testing and unwrapping an Option<T>" is either

match opt_val {
    Some(x) => {
        do_something_with(x);
    }
    None => {}
}

or

if opt_val.is_some() {
    let x = opt_val.unwrap();
    do_something_with(x);
}

In Scala, it would be possible to do exactly the same, but the idiomatic solution is rather to map over an Option (or to foreach if it is only for the side effect of doing_something_with(x)).

Why isn't it an idiomatic solution to do the same in Rust?

opt_val.map(|x| do_something_with(x));
like image 577
bluenote10 Avatar asked Dec 17 '14 20:12

bluenote10


People also ask

Why does rust use let?

The primary use for the let keyword is in let statements, which are used to introduce a new set of variables into the current scope, as given by a pattern. The pattern is most commonly a single variable, which means no pattern matching is done and the expression given is bound to the variable.

How does if let work rust?

The syntax if let takes a pattern and an expression separated by an equal sign. It works the same way as a match , where the expression is given to the match and the pattern is its first arm. In this case, the pattern is Some(max) , and the max binds to the value inside the Some .

What is rust syntax?

Rust is a multi-paradigm programming language like C++ syntax that was designed for performance and safety, especially safe concurrency by using a borrow checker and ownership to validate references.

What is ref in rust?

ref annotates pattern bindings to make them borrow rather than move. It is not a part of the pattern as far as matching is concerned: it does not affect whether a value is matched, only how it is matched.


2 Answers

map() is intended for transforming an optional value, while if let is mostly needed to perform side effects. While Rust is not a pure language, so any of its code blocks can contain side effects, map semantics is still there. Using map() to perform side effects, while certainly possible, will only confuse readers of your code. Note that it should not have performance penalties, at least in simple code - LLVM optimizer is perfectly capable of inlining the closure directly into the calling function, so it turns to be equivalent to a match statement.

Before if let the only way to perform side effects on an Option was either a match or if with Option::is_some() check. match approach is the safest one, but it is very verbose, especially when a lot of nested checks are needed:

match o1 {
    Some(v1) => match v1.f {
        Some(v2) => match some_function(v2) {
            Some(r) => ...
            None => {}
        }
        None => {}
    }
    None => {}
}

Note the prominent rightward drift and a lot of syntactical noise. And it only gets worse if branches are not simple matches but proper blocks with multiple statements.

if option.is_some() approach, on the other hand, is slightly less verbose but still reads very badly. Also its condition check and unwrap() are not statically tied, so it is possible to get it wrong without the compiler noticing it.

if let solves the verbosity problem, based on the same pattern matching infrastructure as match (so it is harder to get wrong than if option.is_some()) and, as a side benefit, allows using arbitrary types in patterns, not only Option. For example, some types may not provide map()-like methods; if let will still work with them very nicely. So if let is a clear win, hence it is idiomatic.

like image 136
Vladimir Matveev Avatar answered Oct 12 '22 09:10

Vladimir Matveev


.map() is specific to the Option<T> type, but if let (and while let!) are features that work with all Rust types.

like image 29
bluss Avatar answered Oct 12 '22 09:10

bluss