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

Not the best example in "6.3 Concise Control Flow with if let". #1401

Closed
andywwright opened this issue Jun 15, 2018 · 19 comments · Fixed by rust-lang/rust#88277
Closed

Not the best example in "6.3 Concise Control Flow with if let". #1401

andywwright opened this issue Jun 15, 2018 · 19 comments · Fixed by rust-lang/rust#88277
Milestone

Comments

@andywwright
Copy link

andywwright commented Jun 15, 2018

Hey guys!

First of all - thank you very much for the great language and the great book. I'm new to coding, so I'm sorry if anything (or everything) I say is stupid.

At least for me, there is a very bad example in the "6.3 Concise Control Flow with if let" chapter. It says:

if let Some(3) = some_u8_value {
    println!("three");
}

I had been somewhat at a loss as to why or how this expression is any better than say

if Some(3) == some_u8_value {
    println!("three");
}

and it took me some time to come to the conclusion that in this particular case it probably isn't better at all, if not worse for arguably "weird" (for me in that state of mind) syntax. By weird, I mean that at the time the part after "if" looked like

let 3 = x

for me, which had just turned my head around :)

Long story short, I think it is more clear to explain "if let" through use of unwrapping the optional value (if it is there), like so:

if let Some(x) = some_u8_value {
    println!("The unwrapped value is {}", x);
}

I hope that makes sense.

@steveklabnik
Copy link
Member

It does, thanks! you're right that being able to use the binding is the key feature of if let in this context. I'll give it some consideration. Thank you!

@Centril
Copy link

Centril commented Sep 27, 2018

My personal inclination is that if let Some(3) = ... { .. } is idiomatic because with Some(3) == ... you are actually creating such a value in memory while with let Some(3) this is just decomposable pattern matching where Some(3) doesn't need to be in memory. While the optimizer will optimize both to the same instructions, == has to go through a detour via the PartialEq trait and so it is a more complicated approach. Using if let Some(3) also shows the power of pattern matching I think.

@andywwright
Copy link
Author

In my humble opinion, we are talking about a book for a newbie here. And we have just told them that "let is used to introduce a binding, to bind some value to a name, so it can be used later".

The thing is, in case of if let Some(3) = ... { .. } this particular let binds nothing and because of that, entirely new (for a newbie) if let syntax could look absurd at the moment.

@imbolc
Copy link
Contributor

imbolc commented Nov 23, 2018

I got if let syntax only in chapter 18 :) Also for me introducing if let in early chapters seems not only confusing but also redundant, as all the examples can probably be rewritten with only match without any drawbacks. Why do we need it there at all?

@hoijui
Copy link

hoijui commented Dec 31, 2018

I am a rust newbie too (Java coder). I love the book, and start to be very excited about the language, and while at times some things are explained maybe a bit to verbosely for me, I would not change that at all... I consider it very good, thank you!
but in this part about if let, I felt totally lost:

  • Wasn't there a normal if already earlier in the book?
  • Or do I remember wrong, and if so, why would the language use if let instead of just if?
  • If they both exist, what is the difference?
  • As there is let in it, which variable is getting assigned here?
    ...

So I give a +1 to @andy-rust and @imbolc here.

@andywwright
Copy link
Author

You'd better think about if let as a one-handed match with no exhaustive checking enforced. In the case of the given example, it has nothing to do with if or let, that's why it is so confusing :)

Maybe it would be better to rename the whole operator, call it match once or something like that.

@hoijui
Copy link

hoijui commented Dec 31, 2018

hmm... thanks @andy-rust !
I wish something like this description would be in the book, but that almost sounds like criticism on the language... ;-)
I guess I will rather use the match + _ approach then. I don't know if there might be an even better name, but I would use it if it was called match once.

@L0uisc
Copy link
Contributor

L0uisc commented Apr 8, 2019

@andywswan what would happen to cases where I want to do if let ... { ... } else { ... } if the name changes to match once? Will the else still work? I realise that the expression can be written as match ... { interesting_case => ..., everything_else => ..., }, but still...

@andywwright
Copy link
Author

@L0uisc sure "else" should work as usual. But it doesn't matter, because everyone seems to be happy with "if let" as it is.

@L0uisc
Copy link
Contributor

L0uisc commented Apr 9, 2019

I am reading the book for the first time and have only advanced to Chapter 10. When I first read about if let, it confused me too. I then stopped and analyzed every part of the "match" which is "equivalent" to if let according to the book. I think one small change would solve the problem for me: include the binding of the some_u8_value in the if let version as well. Maybe, if you believe that is not enough, we can insert an explanation detailing for each part of the if let expression which part of the match expression is its equivalent. (Some places I used "expression" should maybe be "statement." I am not sure which is each of the constructs I am referring to.)

@andywwright
Copy link
Author

No amount of explanations would be enough for me because of the fact, that in the case of if let Some(3) = ... { .. } this let binds no variables. Furthermore, it visibly makes an assignment to a solid type! (U8 or whatever 3 is there after unwrapping the Option). There is no reasonable explanation for this use case to a newbie or to anyone for that matter using just let. And though it can be easily explained with match once and I have successfully done that multiple times with my friends, we can't just put it in The book, can we?

The only right way of proceeding would be to open an RFC with a proposal of replacing this vague if let thing with clear match once or whatever better wording, but I simply do not think there is enough demand to bother the entire society with changing the semantics of The language.

@L0uisc
Copy link
Contributor

L0uisc commented Apr 9, 2019

I think the if is actually not meaning something else here than in other places. The block is executed if the pattern matches and skipped if not. The fact that the let here is not a binding, but syntax for matching a pattern, should be made clearer.

Just FYI: there is while let too, which executes its block while the pattern matches. So I think the best would be to make it clear that the let in if let and while let isn't a binding.

@andywwright
Copy link
Author

the best would be to make it clear that the let in if let and while let isn't a binding.

well, I think more often, than not there will be binding afterall, like if let Some(x) ..., but this is exactly the reason of the confusion in the first place, isn't it?

The book earlier states that "let is used to introduce a binding" so we would have to change it to "let is MOSTLY used to introduce a binding, except for some use cases of if let", which begs the question: why bother to create an exception in the first place? Why do not use a more clear operator?

Just google "rust if let", a lot of folks are greatly confused, some went as far as digging to MIR in attempt to get to the roots of this http://patshaughnessy.net/2018/1/18/learning-rust-if-let-vs--match

@hoijui
Copy link

hoijui commented Apr 15, 2019

could we not at least put a note into the book then, that this might be confusing?
Me, after this, stopped trusting in the book, and stopped reading further, and effectively stopped learning more rust.
I think it would be very good to just admit that this is a somewhat unwisely chosen keyword(-combination),
and that rust is not perfect either, instead of trying to act like this is the most normal thing,
and let the reader doubt his/her own intelligence.

@rthinman
Copy link

rthinman commented Dec 2, 2019

When I saw the example in section 6.3, my head exploded: "there is a r-value on the left side of '=' in an assignment???" After some time grumping, I tried to figure out what is happening, and learned about pattern matching. I still think that if let is confusing syntax, especially for someone coming from language without extensive pattern matching. But given that the language is unlikely to change, here is a three-part suggestion for how to make the book better. (inserted text in italics)

  1. In Chapter 2, where let is introduced (maybe elsewhere, too?), say something like: "... a let statement, which is used to create a variable and bind a value to it. let can also perform pattern matching, which we will discuss more later."

  2. Somewhere before section 6.3, either in Chapter 5 in relation to structs, or as a section between the current "6.1 Defining an Enum" and "6.2 The match Control Flow Operator", add some discussion of destructuring a struct. Here we can talk about let performing binding and pattern matching. It probably fits better in Chapter 6 since its title is "Enums and Pattern Matching".

  3. In the present "6.3 Precise Flow Control with if let", start by reminding the reader that let performs pattern matching as well as binding, and that this is a way to think about this new syntax: if let as a shortcut for match in certain situations. Then when you say, "The syntax if let takes a pattern and an expression ..." the reader will already be thinking, "Oh yeah, let does patterns." And then perhaps add something like, "In this case the pattern is a fixed value, so no variables are bound, but the pattern either matches or not."

@L0uisc
Copy link
Contributor

L0uisc commented Dec 10, 2019

I think it is better to hold off on the destructuring and such until chapter 19 as it is currently. I think there should simply be a paragraph explaining that, for now, if let should be seen as merely syntactic sugar for match { pat => ... , _ => ... , }, with a hint that "more info in chapter 19".

@eddyp
Copy link
Contributor

eddyp commented Dec 12, 2019

Am I mistaken, or is it not possible to have also a binding in an if let, something like:

if let Some(x) = some_u8_value {
    prinntln!("{}", x);
} else {
    println!("No value provided");
}

.. so the confusion is only with the matching pattern part of the language?

with the proposed match once you'd have syntactic sugar, but you would still have use for if let, correct?

@rthinman
Copy link

For me, the confusion was the pattern matching. Destructuring is secondary, as in my mind at least, it is a consequence of patterns and binding. The other confusing thing for me is that in the example, there is no binding because the pattern is a constant, and let had been introduced as a mechanism for binding.

@ClashTheBunny
Copy link

So, I have a possible solution to some of the confusion with this exact statement and it took me a bit to understand.

It would have helped if the previous example with the match statement was shown exactly in the if let version:

    let some_u8_value = Some(0u8);
    match some_u8_value {
        Some(3) => println!("three"),
        _ => (),
    }

paired with

    let some_u8_value = Some(0u8);
    if let Some(3) = some_u8_value {
        println!("three");
    }

That would help parse the new statement as "identical". It took me a long time to figure out that the if let form didn't do the let some_u8_value = ... part also and how it could do that.

@carols10cents carols10cents added this to the ch6 milestone Jul 16, 2021
LeSeulArtichaut added a commit to LeSeulArtichaut/rust that referenced this issue Aug 25, 2021
Update books

## reference

1 commits in 4884fe45c14f8b22121760fb117181bb4da8dfe0..da6ea9b03f74cae0a292f40315723d7a3a973637
2021-07-28 21:31:28 -0700 to 2021-08-19 21:28:10 -0700
- Allow users to change status labels (rust-lang/reference#1083)

## book

7 commits in 7e49659102f0977d9142190e1ba23345c0f00eb1..687e21bde2ea10c261f79fa14797c5137425098d
2021-08-03 21:41:35 -0400 to 2021-08-18 20:48:38 -0400
- Small tweaks to Ferris size and position
- Retain previous height: auto just in case
- Shrink and move ferris when possible
- Snapshot chapter 6 for nostarch
- Demonstrate variable as catch-all for match. Fixes rust-lang/book#1868.
- Improve the if let example to have a binding pattern. Fixes rust-lang/book#1401.
- Fixes typo (rust-lang/book#2816)

## rust-by-example

1 commits in 0dc9cd4e89f00cb5230f120e1a083916386e422b..04f489c889235fe3b6dfe678ae5410d07deda958
2021-07-23 09:14:27 -0300 to 2021-08-17 08:01:20 -0300
- Grammar mistake (rust-lang/rust-by-example#1456)

## rustc-dev-guide

5 commits in c4644b427cbdaafc7a87be0ccdf5d8aaa07ac35f..cf0e151b7925a40f13fbc6573c6f97d5f94c7c17
2021-08-10 20:41:44 +0900 to 2021-08-22 11:47:02 -0300
- Fix typo “a Rc” → “an Rc” (rust-lang/rustc-dev-guide#1191)
- Expand THIR section with more details (rust-lang/rustc-dev-guide#1183)
- Remove docs for old -Z profile-queries flag
- update mdbook version to latest
- allow to quickly edit a page directly on github
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants