The Unclear Impact

I am learning (mostly for FOMO I guess) and it is beautifully constructed. One thing I wonder about, though, is 'let'. We can 'let <identifier> = <expr>'. We can also 'if let <pattern> = <expr>' and 'while let <pattern> = <expr>'. It seems like the 'let' keyword is being used in two completely different ways. And the semantics of using '=' as a comparison instead of using '==' also confuses me. What am I missing?

@jamesvasile In both cases, let acts as a pattern matching operator, although in a bit roundabout way. The key concept is pattern refutability.

Basically, if you’re writing let <identifier> = <expr>, <identifier> acts as the (trivial) pattern that bind the entire rhs to <identifier>. You could also have, say let Some(x) = <expr>, but (outside of an if or a while condition) that would only compile if <expr> is always a Some value. Otherwise the compiler could refute the pattern. So there are two, closely related meanings of let, I guess: as a statement, the pattern must not be refuted at compile time, while in a condition, refutation at runtime causes the conditional to not execute.

This is useful when you want to deconstruct a struct (i.e., product type), because such patterns are never refutable. Example from the Structures chapter of Rust by Example:

struct Point {
    x: f32,
    y: f32,
}

let Point { x: top_edge, y: left_edge } = point;

will assign point.x to top_edge and point.y to left_edge.

This is also very similar to how the pattern matching operator works in Elixir (although Elixir has no let keyword for it and the = operator is always a pattern match, because Elixir has no mutable variables). There, a failed match outside a condition throws a MatchError, since Elixir has no static type checking to validate the pattern at compile time.

replies
1
announces
2
likes
5

Long post

@kristof That is super helpful. Thank you so much for both your written explanation and the pointer into the docs.