Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upDestructuring assignment #372
Comments
This comment has been minimized.
This comment has been minimized.
sbditto85
commented
Dec 30, 2014
|
Not sure the best way to indicate a vote in the affirmative, but |
This comment has been minimized.
This comment has been minimized.
MattWindsor91
commented
Dec 30, 2014
|
Not sure how rust's RFC process goes, but I assume this needs to be written up in the appropriate RFC format first? I like it, mind. |
This comment has been minimized.
This comment has been minimized.
arthurprs
commented
Jan 14, 2015
|
EDIT: not so fond of it anymore |
This comment has been minimized.
This comment has been minimized.
|
@glaebhoerl, how do you expect this to be done? It seems to me that it would require the ability for patterns to appear in arbitrary positions, which strikes me as completely infeasible. |
This comment has been minimized.
This comment has been minimized.
|
@bstrie I don't have any plans myself. There was some discussion of this elsewhere, possibly on the rust repository issues - I think the idea might've been that we could take the intersection of the pattern and expression grammars? |
This comment has been minimized.
This comment has been minimized.
|
Assuming that we took the easy route and made this apply only to assignments, we'd also need to take our grammar from LL(k) to LL(infinity). I also don't think that an arbitrarily restricted pattern grammar will make the language easier to read and understand. Finally, the only time when this feature would be useful is when you can't use a new |
This comment has been minimized.
This comment has been minimized.
DavidJFelix
commented
Jan 19, 2015
|
|
This comment has been minimized.
This comment has been minimized.
tstorch
commented
Jan 21, 2015
|
I would be thrilled if this would be implemented! Here is a small example why: Currently in libcore/str/mod.rs the function fn maximal_suffix(arr: &[u8], reversed: bool) -> (uint, uint) {
let mut left = -1; // Corresponds to i in the paper
let mut right = 0; // Corresponds to j in the paper
let mut offset = 1; // Corresponds to k in the paper
let mut period = 1; // Corresponds to p in the paper
while right + offset < arr.len() {
let a;
let b;
if reversed {
a = arr[left + offset];
b = arr[right + offset];
} else {
a = arr[right + offset];
b = arr[left + offset];
}
if a < b {
// Suffix is smaller, period is entire prefix so far.
right += offset;
offset = 1;
period = right - left;
} else if a == b {
// Advance through repetition of the current period.
if offset == period {
right += offset;
offset = 1;
} else {
offset += 1;
}
} else {
// Suffix is larger, start over from current location.
left = right;
right += 1;
offset = 1;
period = 1;
}
}
(left + 1, period)
}This could easily look like this: fn maximal_suffix(arr: &[u8], reversed: bool) -> (uint, uint) {
let mut left = -1; // Corresponds to i in the paper
let mut right = 0; // Corresponds to j in the paper
let mut offset = 1; // Corresponds to k in the paper
let mut period = 1; // Corresponds to p in the paper
while right + offset < arr.len() {
let a;
let b;
if reversed {
a = arr[left + offset];
b = arr[right + offset];
} else {
a = arr[right + offset];
b = arr[left + offset];
};
// Here is the interesting part
(left, right, offset, period) =
if a < b {
// Suffix is smaller, period is entire prefix so far.
(left, right + offset, 1, right - left)
} else if a == b {
// Advance through repetition of the current period.
if offset == period {
(left, right + offset, 1, period)
} else {
(left, right, offset + 1, period)
}
} else {
// Suffix is larger, start over from current location.
(right, right + 1, 1, 1)
};
// end intereseting part
}
(left + 1, period)
}If we apply, what is currently possible this would be the result: fn maximal_suffix(arr: &[u8], reversed: bool) -> (uint, uint) {
// Corresponds to (i, j, k, p) in the paper
let (mut left, mut right, mut offset, mut period) = (-1, 0, 1, 1);
while right + offset < arr.len() {
let (a, b) =
if reversed {
(arr[left + offset], arr[right + offset])
} else {
(arr[right + offset], arr[left + offset])
};
(left, right, offset, period) =
if a < b {
// Suffix is smaller, period is entire prefix so far.
(left, right + offset, 1, right - left)
} else if a == b {
// Advance through repetition of the current period.
if offset == period {
(left, right + offset, 1, period)
} else {
(left, right, offset + 1, period)
}
} else {
// Suffix is larger, start over from current location.
(right, right + 1, 1, 1)
};
}
(left + 1, period)
}This is easily more readble and I guess readbility of code is a major contribution to code safety and attracts more people to the language and projects written in that laguage. |
This comment has been minimized.
This comment has been minimized.
bombless
commented
Jan 21, 2015
|
It doesn't feel right... introduce a, b;
let (a, b) = returns_tuple(...);
introduce c, d;
let Point { x: c, y: d } = returns_point(...);Still doesn't feel right, but looks more reasonable. |
This comment has been minimized.
This comment has been minimized.
DavidJFelix
commented
Jan 21, 2015
|
So already @bombless this clashes for me as introduce would then become the longest word in rust. |
This comment has been minimized.
This comment has been minimized.
bombless
commented
Jan 21, 2015
|
@DavidJFelix I don't know, I'd say -1 for this assignment idea. |
This comment has been minimized.
This comment has been minimized.
DavidJFelix
commented
Jan 21, 2015
|
@bombless, a bit but not much. The point of "let" isn't to offer assignment, it's to introduce the variable. Assignment is done with an assignment operator, "=", If we use both the "=" and let for assignment, it becomes redundant. This is why you see:
the point of this issue is that "let" allows us to unravel tuple-packed variables as we declare them and also set their value in one assignment, rather than multiple assignments; but later throughout the program, the assignment operator ceases to do this unraveling and must be done for each variable. |
This comment has been minimized.
This comment has been minimized.
taralx
commented
Feb 18, 2015
|
So there's two ways to do this. With a desugaring pass (easier) or by actually extending the implementation of ExprAssign in the typechecker and translation. The former works, but I suspect it doesn't produce as nice a set of error messages when types don't match. Thoughts? |
This comment has been minimized.
This comment has been minimized.
|
I am |
This comment has been minimized.
This comment has been minimized.
sharpjs
commented
Oct 13, 2015
|
let (mut kind, mut ch) = input.classify();
// ... later ...
(kind, ch) = another_input.classify(); |
This comment has been minimized.
This comment has been minimized.
yongqli
commented
Dec 11, 2015
|
|
This comment has been minimized.
This comment has been minimized.
|
Note that this means that in the grammar an assignment statement can take both an expression and a pattern on the lhs. I'm not too fond of that. |
This comment has been minimized.
This comment has been minimized.
taralx
commented
Jan 26, 2016
|
It's not just any expression -- only expressions that result in lvalues, which is probably unifiable with the irrefutable pattern grammar. |
This comment has been minimized.
This comment has been minimized.
yongqli
commented
Jan 27, 2016
|
In the future this could also prevent excessive For example, right now I have code like:
If the compiler understood the concept of a "multi-assignment", in the future this might be written as:
Edit: Now, of course, we can re-write |
This comment has been minimized.
This comment has been minimized.
arthurprs
commented
Jan 27, 2016
|
@yongqli that's very interesting, thanks for sharing |
This comment has been minimized.
This comment has been minimized.
flying-sheep
commented
Feb 17, 2016
|
does this cover let (mut total, mut skipped) = (0, 0);
for part in parts {
(total, skipped) += process_part(part);
} |
This comment has been minimized.
This comment has been minimized.
KalitaAlexey
commented
Feb 17, 2016
|
@flying-sheep You would make this when #953 will landed. |
This comment has been minimized.
This comment has been minimized.
flying-sheep
commented
Feb 17, 2016
|
it’s already accepted, so what’s the harm in including a section about it in this RFC now? |
This comment has been minimized.
This comment has been minimized.
KalitaAlexey
commented
Feb 17, 2016
|
I mean you can do for part in parts {
(total, skipped) += process_part(part);
}Edit: You cannot. Because (total, skipped) creates a tuple. To change previous defined variable you should write for part in parts {
(&mut total, &mut skipped) += process_part(part);
} |
This comment has been minimized.
This comment has been minimized.
|
This is impossible with context-free grammars. In context sensitive grammars, it is entirely possible. It seems that after the I doubt it is possible without making the parser full-blown context sensitive. I could be wrong, though. |
This comment has been minimized.
This comment has been minimized.
flying-sheep
commented
Feb 17, 2016
|
yeah, the
|
This comment has been minimized.
This comment has been minimized.
ldpl
commented
Feb 17, 2016
|
How about adding or reusing a keyword to avoid context-sensitive grammar? For example, "mut" seems to fit well (also reflects let syntax): let a; let b;
mut (a, b) = returns_tuple(...);
let c;
mut Point {x: c, .. } = returns_point(...);
let Point {y: d, .. } = returns_point(...); |
This comment has been minimized.
This comment has been minimized.
KalitaAlexey
commented
Feb 17, 2016
|
I don't like it. I like let (mut a, mut b) = get_tuple();
let SomeStruct(mut value) = get_some_struct();
let Point {x: mut x, .. } = get_point();I don't like let mut a;
let mut b;
(a, b) = get_tuple();I don't like let my_point: Point;
(my_point.x, my_point.y) = returns_tuple(...);I'd like to write let (x, y) = returns_tuple(...);
let my_point = Point {x: x, y: y};I just think that code must be easy readable. |
This comment has been minimized.
This comment has been minimized.
|
@KalitaAlexey, you can already destructure with |
This comment has been minimized.
This comment has been minimized.
torpak
commented
Aug 2, 2017
|
What about using something like tie from c++?
that is just as easy to write and needs no complex extension of the parser. |
This comment has been minimized.
This comment has been minimized.
|
@torpak I thought the parser problem is a solved one: don't try to have full pattern syntax oh the LHS, handle oh the intersection with expressions. |
This comment has been minimized.
This comment has been minimized.
louy2
commented
Sep 15, 2017
|
Is there a properly formatted RFC for this feature yet? |
This comment has been minimized.
This comment has been minimized.
|
I am not aware of any. |
This comment has been minimized.
This comment has been minimized.
c0b
commented
Oct 10, 2017
•
|
so it's only because no RFC for this yet? and no committers have reviewed ? can we call a few of them by mentioning coming from Python world, this is very natural to calculate Fibonacci numbers, by tuple assignment: In [1]: a, b = 1, 1
In [2]: for i in range(10):
...: a, b = b, a+b
...:
In [3]: a, b
Out[3]: (89, 144)and Javascript ES7 does not have tuple, but have object construct and destruct: > let a = 1, b = 1;
undefined
> for (let i in Array.from({length: 10})) {
... ({ a, b } = { a: b, b: a+b });
... }
{ a: 89, b: 144 }and Javascript Array spread assignment, this is valid since ES6 (ES2015) > for (let i = 0; i < 10; i++) [ a, b ] = [ b, a+b ];
[ 89, 144 ] |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Oct 10, 2017
•
|
I dislike that this encourages unneeded mutability. Instead of new assignment syntax for mutable variables, what about some assign/mutate/unlet syntax that locally altered the behavior of patterns from declaration to mutating assignment?
It'd be obnoxious to write I picked a macro syntax here because it's only sugar declaring another binding and assigning to the mutable variable. Also, the |
This comment has been minimized.
This comment has been minimized.
MichaelBell
commented
Dec 3, 2017
|
As a rust newbie I'd just like to add my +1 that it's definitely confusing that you can do I understand there are issues with implementation, but @dgrunwald seems to be saying there's a simple option that would work in the majority of cases that people actually care about - I think that would be worth doing! |
This comment has been minimized.
This comment has been minimized.
louy2
commented
Dec 19, 2017
|
@burdges |
This comment has been minimized.
This comment has been minimized.
phaux
commented
Dec 19, 2017
|
Would it be possible to allow tuples/structs as actual lvalues, so that we don't need to differentiate between pattern/expr at all? |
This comment has been minimized.
This comment has been minimized.
|
@phaux I would hope so (syntactically), preferably in a way that |
This comment has been minimized.
This comment has been minimized.
|
Hm, now that's an idea, though I'm not sure how backwards compatible it would be: let a = 5;
let b = "hello".to_string();
let (ref x, ref y) = (a, b); // this would move `a` and `b` today, but just reference them with constructors becoming lvalues.That said, I think it wouldn't work anyway, as you would need a single canonical address for a constructor lvalue - which means constructing it from other lvalues would not really work (unless we special case it in the language, such that you get only an error if you attempt to take the address of a constructor lvalue, or magically let it behave as a rvalue in that case) |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
phaux
commented
Dec 19, 2017
|
@eddyb Exactly. |
This comment has been minimized.
This comment has been minimized.
|
Ah, then I misunderstood. So basically one of the things which have been proposed already. |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Dec 19, 2017
•
|
If I'm still nervous about encouraging mutability, but could the syntactic issue be handled by "commuting" the let a; // uninitialized here
let mut b = bar(); // mutable
let c = baz() : &mut u64; // mutable reference
virtual Foo { a, b, *c, let d, let mut e } = foo(); // named field puns
// only a and d are immutable You could replace |
This comment has been minimized.
This comment has been minimized.
|
@burdges Nobody is encouraging mutability, just bringing the cases where you need to mutate closer to those where you can just bind the resulting value. As for temporaries, Also, I'd expect this to "just work" (to initialize the two new variables): let (d, mut e);
Foo { a, b, *c, d, e } = foo();And I have no idea what you mean by that |
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Dec 20, 2017
|
Is anything preventing this moving to RFC stage, or has no one simply taken up the task of writing one yet? |
This comment has been minimized.
This comment has been minimized.
apiraino
commented
Feb 14, 2018
•
|
Hello, I didn't follow the whole discussion, but I agree with @MichaelBell this is a bit confusing (me newbie too). It took me a good deal of time before arriving here and finally understand what was happening. In this test case the situation is simpler and the compiler (as of Feb, 2018) warns me about "variables not needing to be mutable", that did sound strange to me. |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Feb 14, 2018
|
I seemingly missed that comment. I meant In essence, |
Centril
added
the
T-lang
label
Feb 14, 2018
This comment has been minimized.
This comment has been minimized.
noelzubin
commented
Sep 25, 2018
|
any update on this ? |
This comment has been minimized.
This comment has been minimized.
|
If someone wanted to write an RFC taking the points in this thread into account, I'm sure it'd be well-received. |
Centril
added
A-syntax
A-expressions
labels
Nov 27, 2018
This comment has been minimized.
This comment has been minimized.
Walther
commented
Feb 27, 2019
|
Wrote an initial draft of the RFC here based on this thread. Comments would be super helpful, thank you! |
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Feb 27, 2019
|
Thanks @Walther, that's super. |
glaebhoerl commentedOct 8, 2014
Given
it would be nice to be able to do things like
and not just in
lets, as we currently allow.Perhaps even:
(Most use cases would likely involve
mutvariables; but those examples would be longer.)Related issues from the
rustrepo: rust-lang/rust#10174 rust-lang/rust#12138