-
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
Destructuring assignment #372
Comments
Not sure the best way to indicate a vote in the affirmative, but 👍 +1 for this |
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. |
EDIT: not so fond of it anymore |
@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. |
@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? |
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 |
👍 I've found myself wanting this from time to time, especially in reducing repetition in match statements or normal assignment. Right now I'm using small purpose-built functions instead of this. I haven't considered if it would be possible to abuse a feature like this easily or not. |
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. |
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. |
So already @bombless this clashes for me as introduce would then become the longest word in rust. |
@DavidJFelix I don't know, I'd say -1 for this assignment idea. |
@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. |
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? |
I am 👍 for this too |
👍 Ran into this today. I'm surprised that it's not implemented already. A function can return a tuple. If I can bind that tuple via a destructuring let (mut kind, mut ch) = input.classify();
// ... later ...
(kind, ch) = another_input.classify(); |
👍 I would love to see this implemented. |
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. |
It's not just any expression -- only expressions that result in lvalues, which is probably unifiable with the irrefutable pattern grammar. |
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 |
@yongqli that's very interesting, thanks for sharing |
does this cover let (mut total, mut skipped) = (0, 0);
for part in parts {
(total, skipped) += process_part(part);
} |
@flying-sheep You would make this when #953 will landed. |
it’s already accepted, so what’s the harm in including a section about it in this RFC now? |
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 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. |
yeah, the
|
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(...); |
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. |
@KalitaAlexey, you can already destructure with |
Is anything preventing this moving to RFC stage, or has no one simply taken up the task of writing one yet? |
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. |
I seemingly missed that comment. I meant In essence, |
any update on this ? |
If someone wanted to write an RFC taking the points in this thread into account, I'm sure it'd be well-received. |
Wrote an initial draft of the RFC here based on this thread. Comments would be super helpful, thank you! |
Thanks @Walther, that's super. |
…ords [RFC ember-data] modelFactoryFor
…ntril Improve diagnostics for invalid assignment - Improve wording and span information for invalid assignment diagnostics. - Link to rust-lang/rfcs#372 when it appears the user is trying a destructuring assignment. - Make the equality constraint in `where` clauses error consistent with the invalid assignment error.
…ntril Improve diagnostics for invalid assignment - Improve wording and span information for invalid assignment diagnostics. - Link to rust-lang/rfcs#372 when it appears the user is trying a destructuring assignment. - Make the equality constraint in `where` clauses error consistent with the invalid assignment error.
@fanzier, @cartesiancat and I are going to tackle this feature. We're going to start with an implementation, so we can establish the feasibility, and then follow up with a formal RFC once everything appears to be working. |
@varkor Thanks, I'm looking forward to it, I often wished this was already possible. |
@fanzier and I have opened an RFC for destructuring assignment. We also have a working prototype at rust-lang/rust#71156. |
I'm looking forward to this feature, too. For my question: use std::net::{ UdpSocket };
fn main() -> std::io::Result<()> {
let mut buf: Vec<u8> = Vec::new();
let socket = UdpSocket::bind("0.0.0.0:8080")?;
let (_len, src) = socket.recv_from(&mut buf)?;
loop {
// A resp Packet::Data to B
socket.send_to(&buf, src)?;
// B send Packet::Ack to A
let (_len, src) = socket.recv_from(&mut buf)?;
}
} There will be a error for: 'src' inside the loop: note: But if we can destructuring assignment: use std::net::{ UdpSocket, SocketAddr};
fn main() -> std::io::Result<()> {
let mut buf: Vec<u8> = Vec::new();
let socket = UdpSocket::bind("0.0.0.0:8080")?;
let (mut len, mut src): (usize, SocketAddr) = socket.recv_from(&mut buf)?;
loop {
// A resp Packet::Data to B
socket.send_to(&buf, src)?;
// B send Packet::Ack to A
(len, src) = socket.recv_from(&mut buf)?;
}
} Instead of that as following, something seems verbose: fn main() -> std::io::Result<()> {
let mut buf: Vec<u8> = Vec::new();
let socket = UdpSocket::bind("0.0.0.0:8080")?;
let (mut _len, mut src): (usize, SocketAddr) = socket.recv_from(&mut buf)?;
loop {
// A resp Packet::Data to B
socket.send_to(&buf, src)?;
// B send Packet::Ack to A
let (_len, isrc) = socket.recv_from(&mut buf)?;
src = isrc;
}
} |
Given
it would be nice to be able to do things like
and not just in
let
s, as we currently allow.Perhaps even:
(Most use cases would likely involve
mut
variables; but those examples would be longer.)Related issues from the
rust
repo: rust-lang/rust#10174 rust-lang/rust#12138The text was updated successfully, but these errors were encountered: