-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Tracking issue for pattern with by-move & by-ref bindings #68354
Comments
#![feature(move_ref_pattern)]
)
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Initial implementation of `#![feature(move_ref_pattern)]` Following up on #45600, under the gate `#![feature(move_ref_pattern)]`, `(ref x, mut y)` is allowed subject to restrictions necessary for soundness. The match checking implementation and tests for `#![feature(bindings_after_at)]` is also adjusted as necessary. Closes #45600. Tracking issue: #68354. r? @matthewjasper
As of now, we don't believe more tests would needed before we stabilize, but if you can think of some, please do add or propose them. What we're waiting now is probably for more time to pass. |
When could this feature potentially be stabilized, assuming no (major) issues are found? Of course the response might be "just use another control flow structure, e.g. if-let", and in principle I could do that. In practice though, because it is code generation code, debugging is harder, and I'd prefer to keep the current code intact as much as possible. So much so that I'd rather wait for this feature to become stable if the amount of time it will take to stabilize is not too great. |
So this feature hasn't seen any dogfooding inside the compiler itself, and I don't know what the testing situation in the ecosystem is like. However, the |
Meanwhile, we're a number of months further, the world is different, and I just ran into this very same issue again. |
Especially since the test suite is extensive, shouldn't this be stabilizable in time for the next stable release? |
Nominating for T-lang to discuss stabilization. |
Discussing in the @rust-lang/lang meeting today: We would love to see this stabilized. @matthewjasper if you or someone else wants to prepare a PR and stabilization report (showing interesting test cases and with a short description of the behavior to be stabilized), that would be great! One thing that would be nice is seeing links to this feature being used "in the wild" (i.e., in the ecosystem), but maybe we don't know of any projects. (One could search for the feature gate...) |
It looks like there are no uses of the feature gate outside of rustc and announcements/metadata. That shouldn't necessarily stop us going forward, but it seems worth noting. This is the kind of feature, though, that people won't necessarily use nightly just to get. |
I've consistently been asking for this feature precisely because I want to use it in the wild :) As for how this pattern appears in my code in the first place, when possible I often
Quite true. The |
I should say that https://grep.app only scans about one million repositories on GitHub. |
FWIW I ran into this issue today when fetching the status of some process running on a server in a loop and trying to match a #[derive(PartialEq, Eq)]
struct Finished {}
#[derive(PartialEq, Eq)]
struct Processing {
status: ProcessStatus,
}
#[derive(PartialEq, Eq)]
enum ProcessStatus {
One,
Two,
Three,
}
#[derive(PartialEq, Eq)]
enum Status {
Finished(Finished),
Processing(Processing),
}
fn check_result(_url: &str) -> Status {
// fetch status from some server
Status::Processing(Processing {
status: ProcessStatus::One,
})
}
fn wait_for_result(url: &str) -> Finished {
let mut previous_status = None;
loop {
match check_result(url) {
Status::Finished(f) => return f,
Status::Processing(p) => {
match (&mut previous_status, p.status) {
(None, status) => previous_status = Some(status), // first status
(Some(previous), status) if *previous == status => {} // no change, ignore
(Some(previous), status) => { // error!
// new status
*previous = status;
}
}
}
}
}
} In my case the fix was easy enough, I realized I want to do the same processing that would go in the new status case with the first status as well, so I could just do (Some(previous), status) if *previous == status => {} // no change, ignore
(_, status) => {
// new status
previous_status = Some(status);
} Though in my case a workaround was trivial, this is what a use in the wild might've looked like if this was in stable today so I thought I'd post it. It felt like something that should work, so I'm glad to see that work is underway to stabilize it even if I'm not necessarily in a hurry to use it myself. Thanks! |
Tagging with E-mentor -- this needs a stabilization PR, as described here: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html The PR should include a brief report, but it doesn't have to go into details. The short version is:
|
@niko I've taken a quick look at what's involved in writing such a stabilization PR. Is there a possibility to perhaps have 2 or more people share the honor and responsibility? |
@jjpe seems like that would be fine to me, if somebody else wants to help out! |
Hi @nikomatsakis @jjpe, I'm interested in helping with stabilizing this feature. |
While I love that there are now people willing to take this up, in the past month life has gotten much, much busier for me. |
@rustbot claim |
I'll start working on stabilizing this feature |
Process for stabilization:
|
Does this feature removes Error Example: #![feature(bindings_after_at)]
fn main() {
let x = Some("s".to_string());
match x {
op_string @ Some(s) => {},
//~^ ERROR E0007
//~| ERROR E0382
None => {},
}
} Error:
|
…n, r=nikomatsakis Stabilize move_ref_pattern # Implementation - Initially the rule was added in the run-up to 1.0. The AST-based borrow checker was having difficulty correctly enforcing match expressions that combined ref and move bindings, and so it was decided to simplify forbid the combination out right. - The move to MIR-based borrow checking made it possible to enforce the rules in a finer-grained level, but we kept the rule in place in an effort to be conservative in our changes. - In rust-lang#68376, @Centril lifted the restriction but required a feature-gate. - This PR removes the feature-gate. Tracking issue: rust-lang#68354. # Description This PR is to stabilize the feature `move_ref_pattern`, which allows patterns containing both `by-ref` and `by-move` bindings at the same time. For example: `Foo(ref x, y)`, where `x` is `by-ref`, and `y` is `by-move`. The rules of moving a variable also apply here when moving *part* of a variable, such as it can't be referenced or moved before. If this pattern is used, it would result in *partial move*, which means that part of the variable is moved. The variable that was partially moved from cannot be used as a whole in this case, only the parts that are still not moved can be used. ## Documentation - The reference (rust-lang/reference#881) - Rust by example (rust-lang/rust-by-example#1377) ## Tests There are many tests, but I think one of the comperhensive ones: - [borrowck-move-ref-pattern-pass.rs](https://github.com/Centril/rust/blob/85fbf49ce0e2274d0acf798f6e703747674feec3/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs) - [borrowck-move-ref-pattern.rs](https://github.com/Centril/rust/blob/85fbf49ce0e2274d0acf798f6e703747674feec3/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.rs) # Examples ```rust #[derive(PartialEq, Eq)] struct Finished {} #[derive(PartialEq, Eq)] struct Processing { status: ProcessStatus, } #[derive(PartialEq, Eq)] enum ProcessStatus { One, Two, Three, } #[derive(PartialEq, Eq)] enum Status { Finished(Finished), Processing(Processing), } fn check_result(_url: &str) -> Status { // fetch status from some server Status::Processing(Processing { status: ProcessStatus::One, }) } fn wait_for_result(url: &str) -> Finished { let mut previous_status = None; loop { match check_result(url) { Status::Finished(f) => return f, Status::Processing(p) => { match (&mut previous_status, p.status) { (None, status) => previous_status = Some(status), // first status (Some(previous), status) if *previous == status => {} // no change, ignore (Some(previous), status) => { // Now it can be used // new status *previous = status; } } } } } } ``` Before, we would have used: ```rust match (&previous_status, p.status) { (Some(previous), status) if *previous == status => {} // no change, ignore (_, status) => { // new status previous_status = Some(status); } } ``` Demonstrating *partial move* ```rust fn main() { #[derive(Debug)] struct Person { name: String, age: u8, } let person = Person { name: String::from("Alice"), age: 20, }; // `name` is moved out of person, but `age` is referenced let Person { name, ref age } = person; println!("The person's age is {}", age); println!("The person's name is {}", name); // Error! borrow of partially moved value: `person` partial move occurs //println!("The person struct is {:?}", person); // `person` cannot be used but `person.age` can be used as it is not moved println!("The person's age from person struct is {}", person.age); } ```
…n, r=nikomatsakis Stabilize move_ref_pattern # Implementation - Initially the rule was added in the run-up to 1.0. The AST-based borrow checker was having difficulty correctly enforcing match expressions that combined ref and move bindings, and so it was decided to simplify forbid the combination out right. - The move to MIR-based borrow checking made it possible to enforce the rules in a finer-grained level, but we kept the rule in place in an effort to be conservative in our changes. - In rust-lang#68376, @Centril lifted the restriction but required a feature-gate. - This PR removes the feature-gate. Tracking issue: rust-lang#68354. # Description This PR is to stabilize the feature `move_ref_pattern`, which allows patterns containing both `by-ref` and `by-move` bindings at the same time. For example: `Foo(ref x, y)`, where `x` is `by-ref`, and `y` is `by-move`. The rules of moving a variable also apply here when moving *part* of a variable, such as it can't be referenced or moved before. If this pattern is used, it would result in *partial move*, which means that part of the variable is moved. The variable that was partially moved from cannot be used as a whole in this case, only the parts that are still not moved can be used. ## Documentation - The reference (rust-lang/reference#881) - Rust by example (rust-lang/rust-by-example#1377) ## Tests There are many tests, but I think one of the comperhensive ones: - [borrowck-move-ref-pattern-pass.rs](https://github.com/Centril/rust/blob/85fbf49ce0e2274d0acf798f6e703747674feec3/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs) - [borrowck-move-ref-pattern.rs](https://github.com/Centril/rust/blob/85fbf49ce0e2274d0acf798f6e703747674feec3/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.rs) # Examples ```rust #[derive(PartialEq, Eq)] struct Finished {} #[derive(PartialEq, Eq)] struct Processing { status: ProcessStatus, } #[derive(PartialEq, Eq)] enum ProcessStatus { One, Two, Three, } #[derive(PartialEq, Eq)] enum Status { Finished(Finished), Processing(Processing), } fn check_result(_url: &str) -> Status { // fetch status from some server Status::Processing(Processing { status: ProcessStatus::One, }) } fn wait_for_result(url: &str) -> Finished { let mut previous_status = None; loop { match check_result(url) { Status::Finished(f) => return f, Status::Processing(p) => { match (&mut previous_status, p.status) { (None, status) => previous_status = Some(status), // first status (Some(previous), status) if *previous == status => {} // no change, ignore (Some(previous), status) => { // Now it can be used // new status *previous = status; } } } } } } ``` Before, we would have used: ```rust match (&previous_status, p.status) { (Some(previous), status) if *previous == status => {} // no change, ignore (_, status) => { // new status previous_status = Some(status); } } ``` Demonstrating *partial move* ```rust fn main() { #[derive(Debug)] struct Person { name: String, age: u8, } let person = Person { name: String::from("Alice"), age: 20, }; // `name` is moved out of person, but `age` is referenced let Person { name, ref age } = person; println!("The person's age is {}", age); println!("The person's name is {}", name); // Error! borrow of partially moved value: `person` partial move occurs //println!("The person struct is {:?}", person); // `person` cannot be used but `person.age` can be used as it is not moved println!("The person's age from person struct is {}", person.age); } ```
…n, r=nikomatsakis Stabilize move_ref_pattern # Implementation - Initially the rule was added in the run-up to 1.0. The AST-based borrow checker was having difficulty correctly enforcing match expressions that combined ref and move bindings, and so it was decided to simplify forbid the combination out right. - The move to MIR-based borrow checking made it possible to enforce the rules in a finer-grained level, but we kept the rule in place in an effort to be conservative in our changes. - In rust-lang#68376, @Centril lifted the restriction but required a feature-gate. - This PR removes the feature-gate. Tracking issue: rust-lang#68354. # Description This PR is to stabilize the feature `move_ref_pattern`, which allows patterns containing both `by-ref` and `by-move` bindings at the same time. For example: `Foo(ref x, y)`, where `x` is `by-ref`, and `y` is `by-move`. The rules of moving a variable also apply here when moving *part* of a variable, such as it can't be referenced or moved before. If this pattern is used, it would result in *partial move*, which means that part of the variable is moved. The variable that was partially moved from cannot be used as a whole in this case, only the parts that are still not moved can be used. ## Documentation - The reference (rust-lang/reference#881) - Rust by example (rust-lang/rust-by-example#1377) ## Tests There are many tests, but I think one of the comperhensive ones: - [borrowck-move-ref-pattern-pass.rs](https://github.com/Centril/rust/blob/85fbf49ce0e2274d0acf798f6e703747674feec3/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs) - [borrowck-move-ref-pattern.rs](https://github.com/Centril/rust/blob/85fbf49ce0e2274d0acf798f6e703747674feec3/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.rs) # Examples ```rust #[derive(PartialEq, Eq)] struct Finished {} #[derive(PartialEq, Eq)] struct Processing { status: ProcessStatus, } #[derive(PartialEq, Eq)] enum ProcessStatus { One, Two, Three, } #[derive(PartialEq, Eq)] enum Status { Finished(Finished), Processing(Processing), } fn check_result(_url: &str) -> Status { // fetch status from some server Status::Processing(Processing { status: ProcessStatus::One, }) } fn wait_for_result(url: &str) -> Finished { let mut previous_status = None; loop { match check_result(url) { Status::Finished(f) => return f, Status::Processing(p) => { match (&mut previous_status, p.status) { (None, status) => previous_status = Some(status), // first status (Some(previous), status) if *previous == status => {} // no change, ignore (Some(previous), status) => { // Now it can be used // new status *previous = status; } } } } } } ``` Before, we would have used: ```rust match (&previous_status, p.status) { (Some(previous), status) if *previous == status => {} // no change, ignore (_, status) => { // new status previous_status = Some(status); } } ``` Demonstrating *partial move* ```rust fn main() { #[derive(Debug)] struct Person { name: String, age: u8, } let person = Person { name: String::from("Alice"), age: 20, }; // `name` is moved out of person, but `age` is referenced let Person { name, ref age } = person; println!("The person's age is {}", age); println!("The person's name is {}", name); // Error! borrow of partially moved value: `person` partial move occurs //println!("The person struct is {:?}", person); // `person` cannot be used but `person.age` can be used as it is not moved println!("The person's age from person struct is {}", person.age); } ```
I have been using this feature in |
No worries, it is stable now! The feature was stabilized in #76119 and was released last month in stable rust 1.49 https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1490-2020-12-31 . It should be safe to close this tracking issue now ^^ |
This is the tracking issue for
#![feature(move_ref_pattern)]
, which allows patterns containing both by-ref and by-move bindings at the same time. For example:(ref x, y, ref mut z)
.Implementation history
#![feature(move_ref_pattern)]
#68376 landed, thereby fixing Pattern errors are too imprecise and should be removed in favor of MIR borrowck #45600. The PR was written by @Centril and reviewed by @matthewjasper.The text was updated successfully, but these errors were encountered: