Probably we have that difference because I don’t rely on intuition when reading code. I don’t associate “human” and “programming” language, so I see for x in y without implied “each”, and if x in y without “some/this”, therefore they only differs in “if/for” prefix for me.
I’m sure that my examples are invalid, but at least they should demonstrate the idea:
x in it;
⇒
let __i_1: Iterator<Item=i32> = IntoIterator::into_iter(it);
match __i_1.next() {
Some(x) => { // WARNING: unused variable `x`
true
},
None => {
false
},
};
Here __i_1 is autogenerated unique name. It also should be invisible further, but I dont know how to express that
let y = x in it;
let z = x in it;
⇒
let __i_1: Iterator<Item=i32> = IntoIterator::into_iter(it);
let y = match __i_1.next() {
Some(x) => { // WARNING: unused variable `x`
true
},
None => {
false
},
};
let __i_1: Iterator<Item=i32> = IntoIterator::into_iter(it); // ERROR: use of moved value `it`
let z = match __i_2.next() {
Some(x) => { // WARNING: unused variable `x`
true
},
None => {
false
},
};
This just demonstrates how moving and borrowing should work.
iter::once(x in it);
⇒
let __i_1: Iterator<Item=i32> = IntoIterator::into_iter(it);
iter::once(match __i_1.next() {
Some(x) => { // WARNING: unused variable `x`
true
},
None => {
false
},
});
Desugaring in if context will be different:
let x = if x in it {
do_something(x)
} else {
default()
};
⇒
let __i_1: Iterator<Item=i32> = IntoIterator::into_iter(it);
let x = match __i_1.next() {
Some(x) => {
do_something(x)
},
None => {
default()
},
};
It will be context dependent, e.g. if x in it is desugared differently than x in it
Since for and while will be the same (loop until condition is true), there will be only one example that uses while:
while x in it {
do_something(x)
}
⇒
let __i_1 = IntoIterator::into_iter(it);
loop {
match __i_1.next() {
Some(x) => {
do_something(x)
},
None => {
},
}
}
What I wanted to say is that syntax proposed by me is similar to <expr> is <pat> that was proposed and partially implemented by @petrochenkov, as alternative to if let with multiple patterns.
Expressions like x in it will be similar to iterator_on_it.next() is Some(x), so there might be common parts, like integration with && and ||.
I guess, desugaring might rely on turning && operator placed near x in it expressions into nested if/match expressions (and probably that will be more than syntax sugar, because we need to flatten all generated else branches into one). Desugaring with || operator is another challenge, because that syntax by itself might introduce indeterminism in cases like (x in it1 || true) && x.use(), and that should be prohibited (here’s my attempt to describe how); additionally all generated if→then branches should be flatten into one.
Some naive examples:
if x in it && x.cond() {
do_something(x);
}
⇒
let __i_1 = IntoIterator::into_iter(it);
match __i_1.next() {
Some(x) => {
if x.cond() {
do_something(x);
} else {
}
},
None => {
}
}
let y = if cond() && x in it {
do_something(x)
} else {
default()
};
⇒
let __i_1 = IntoIterator::into_iter(it);
if cond() {
match __i_1.next() {
Some(x) => {
do_something(x)
},
None => {
// FLATTEN with `else` below
}
}
} else {
default()
};
let y = if x in it1 || x in it2 {
do_something(x)
} else {
default()
};
⇒
let __i_1 = IntoIterator::into_iter(it1);
let __i_2 = IntoIterator::into_iter(it2);
let y = match __i_1.next() {
Some(x) => {
do_something(x)
},
None => {
match __i_2.next() {
Some(x) => {
// FLATTEN with `then` above
},
None => {
default()
}
}
}
}
Below is complex example that demonstarates idea how to flatten branches using Option type:
let y = if (x in it1 && x.cond() || cond() && x in it2) && x.cond2() {
do_something(x)
} else {
default()
};
⇒
let __i_1 = IntoIterator::into_iter(it1);
let __i_2 = IntoIterator::into_iter(it2);
let y = {
let x = match __i_1.next() { // `(x in it1`
Some(x) => {
if x.cond() { // `&& x.cond()`
Some(x)
} else {
None
}
},
None => {
None
},
};
let x = match x { // `||`
Some(x) => {
Some(x)
},
None => {
if cond() { // `cond() &&`
match __i_2.next() { // `x in it2)`
Some(x) => {
Some(x)
},
None => {
None
},
}
} else {
None
}
},
};
let y = match x { // `&&`
Some(x) => {
if x.cond2() { // `x.cond2()
Some({ // `{`
do_something(x)
}) // `}`
} else {
None
}
},
None => {
None
},
};
match y {
Some(y) => {
y
},
None => { // `else {`
default()
}, // `}`
}