-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Support && in if let expressions #929
Comments
I don't see how '&&' adds anything since you can nest patterns, i.e. if let Some(Some(i)) = op_op {
println!("Matched {:?}, {:?}!", op, i);
} '||' is basically there as well but its through '|' though it doesn't work for 'if let' yet. |
@Marwes Sorry. Maybe I simplified too much. The idea was to make this reddit problem a little nicer. In his case, he has more different Let me know if this example is better. I can modify the header problem then. enum Enum {
Null,
Cons(Box<Enum>),
}
fn main() {
let deep = Enum::Cons(Box::new(
Enum::Cons(Box::new(
Enum::Cons(Box::new(
Enum::Null))))));
// There are two issues. First is that nesting requires a dereference
// every time. The second is that when structured like this, every `if let`
// requires a separate `else` condition even if they are all identical.
if let Enum::Cons(far_outer) = deep {
if let Enum::Cons(outer) = *far_outer {
if let Enum::Cons(inner) = *outer {
if let Enum::Null = *inner {
println!("Null!");
} else { println!("Failed!") }
} else { println!("Failed!") }
} else { println!("Failed!") }
} else { println!("Failed!") }
// This would avoid them.
if let Enum::Cons(far_outer) = deep &&
Enum::Cons(outer) = *far_outer &&
Enum::Cons(inner) = *outer &&
Enum::Null = *inner {
println!("Null!");
} else { println!("Failed!") }
} |
Also, fn main() {
let three = Some(3);
let four = Some(4);
// `|` is being used on alternations of a *single* destructure.
// Destructure `three` and see if it matches either available
// alternatives
if let Some(3) | Some(4) = three { println!("Found 3 or 4"!) }
// `||` is being used on *different* destructures of different variables.
// Try to destructure `three` and if it fails, then try destructuring a
// different variable `four` to determine if it matches a second condition.
if let Some(3) = three ||
Some(4) = four { println!("Found 3 or 4!") }
} |
I discovered it's possible to implement use std::ops::Deref;
#[derive(Debug)]
enum Enum {
Num(End),
Cons(Box<Enum>),
}
#[derive(Debug)]
struct End(i32);
impl Deref for Enum {
type Target = End;
fn deref(&self) -> &End {
match *self {
Enum::Cons(ref b) => b.deref(),
Enum::Num(ref i) => i,
}
}
}
fn main() {
let deep = Enum::Cons(Box::new(
Enum::Cons(Box::new(
Enum::Num(End(3))))));
match *deep {
End(i) => println!("Got `i`: {}", i),
}
} |
if let a = foo(), let b = a.bar(), let c = b.baz() where c > 2, let d = c.quux() {
// all operations succeeded
} else {
// something failed
} (which actually can be shortened slightly, the 2nd and 3rd I'm inclined to say we should do something similar. I think this syntax makes more sense than trying to conceptually overload |
That's better than my idea. |
A more general feature that would allow this would be making match foo() {
Some(i) if let Some(num) = i.parse::<i32>() => ...,
Some(i) if let Some(num) = i.parse::<f64>() => ...,
...
} Used inside if let (((a if let b = a.bar()) if let c = b.baz()) if let d = c.quux()) = foo() {
// all operations succeeded
} else {
// something failed
} Although that is a lot less readable than @kballard’s syntax, it’s more general. I’d probably prefer having both features: |
@P1start it's more general, but as you say, it's less readable. I'm also concerned that it would not interact well with the restriction that you cannot bind by-move into a pattern guard. Note that this restriction is in place even if no other patterns attempt to bind the value in question. That restriction means you cannot replicate Swift 1.2's That said, I'm inclined to say that maybe we should allow |
I want this. I've got quite some cases where I need intermediate processing between the destructuring (which is why the nested patterns don't apply so much) or need multiple results from destructuring at once. In those cases, the nests go really deep. |
Just ran into this issue myself. It would be immensely useful. In fact, as evidence of the usefulness (and feasibility) of this feature. Check out Manishearth/rust-clippy which utilizes a macro called if_let_chain! quite extensively: |
Another option would be making
would make
would make For
is equivalent to Etc. This requires much less special casing so it can just work everywhere. And the compiler can warn about |
I would also like this feature. |
this would be great! |
Just ran into this today. I really like the Swift syntax. |
This is my first day using Rust and I already ran into the need for this feature.. :) |
Another solution is the "swift guard" style statement, it seems to be all the rage in the swift community. guard let x = x else {return false}
// x is now more > 0 For Rust that could look something like: guard let Ok(mut reader) = fs::File::open(&filename) else {return false};
// reader is now defined |
The feature is already there basically, because Rust has tuples: let x : Option<&str> = Some("a");
let y : Option<&str> = Some("b");
if let (Some(a),Some(b)) = (x,y) {
println!("got a={}, b={}",a,b);
}
else {
println!("a and b are not both defined");
} |
This is a misunderstanding of the main linked post and is hardly equivalent. It completely lacks the ability to apply functionality to destructured types and then continue to destructure them without entering a new block. The original post would have shared these abilities but doesn't have as clean a form. |
It looks like C# 7.0 supports the equivalent of 'let' in an expression:
So it's not completely insane. If |
The example @comex gave is exactly what I'd like non-exhaustive pattern matching to look in Rust
and adding this to Proof-of-concept implementation is available on my branch! |
Hey, any updates on this? |
Ran into this on my first days programming in Rust. Here are my thoughts in hope of future improvements. So, currently Rust has two kinds of First problem: that's weird because both For the first: if boolean_flag if let Some(thing) = get_it() { ... } else { ... }
if let Some(thing) = get_it() if boolean_flag { ... } else { ... } That seems to be the most "rusty" way because similar syntax yet possible in if is_a()
if is_b() {
...
}
if let Ok(a) = try_get_a()
if let Ok(b) = try_get_b() {
...
} completely valid, which from my point of view don't look so bad. if (get_a() && get_b()) || get_c()
if is_something_valid()
if let Some(x) = get_it() {
...
} Code formatters could keep these For the second: if let
Some(a) = get_opt_a(),
Ok(b) = try_get_b(a),
Some(c) = local_value {
...
} I'm against reusing For the third: if let
!Err(_) = try_prepare(),
Ok(something) = try_get() {
...
} |
"Error" handling could be made pretty (ref #929 (comment)): (let (Some(a), Some(b)) = (x, y)) || {
println!("failed to unpack x and y");
return;
};
// use `a` here, without causing an additional level of indentation/nesting. (The alternative being let a;
let b;
if let (Some(x1), Some(y1)) = (x, y) {
a = x1;
b = y1;
} else {
println!("failed to unpack x and y");
return;
}
// use `a` here, without causing an additional level of indentation/nesting. Which is just... so much more verbose that I'd much rather have the former.) |
Notifying on this thread as well: proof-of-concept implementation of #929 (comment) is available, see #2260 (comment) for detailed description. |
Closing in favor of accepted RFC #2497. |
Update:
@kballard has a better suggestion than I did at #929 (comment) . Similar type of idea but borrows from Swift.
Original idea below.
Seems like a good idea to support
&&
inif let
expressions. I'm not sure about||
. It seems good from a consistency standpoint but I'm not sure if the the fact that the destructuring could result in different types could present a problem or not. In #525 it was a problem so I'd anticipate a problem here as well.Technically,
&&
is typically used withbool
so&
or some other identifier (and
?) could be valid here as well. I thought&&
made sense though because this is being interpreted in a slightly similar fashion to a boolean expression.EDIT: Added
else
to examples and an example of a deeper nesting example.The text was updated successfully, but these errors were encountered: