-
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
Request: Refactor option to extract a non-capture closure into a function #8811
Comments
Obviously this is challenging in the general case, primarily due to capturing. However, doing this if the closure is non-capturing should work fine. |
@DJMcNab For capturing case, can it just add parameters to the created function? |
Then you wouldn't get rid of the closure though as it would have to wrap and call the created function again at which point you can just use the extract function assist. Also note that we do not have capturing analysis yet. |
@Veykril I see. |
I think for now, I'd just ignore captures, and when we have capture analysis, we should disable the assist if the closure has captures. (Extracting a function with parameters for the captures can be done with the existing "Extract function" assist.) |
My current workaround is to only select the closure body ( numbers // Vec<i32>
.into_iter()
.filter(|x| *x % 2 == 0)
.collect::<Vec<_>>() gets turned into numbers // Vec<i32>
.into_iter()
.filter(|x| predicate(x))
.collect::<Vec<_>>()
fn predicate(x: &i32) {
*x % 2 == 0
} and then to replace the unnecessary |
That appears to be issue #17579 and #17587 implemented rename on extract but for variables and the PR author appears interested in adding it also for function extract #17579 (comment). |
Regarding Capture analysis wouldn't it work to check that the set of variables in the arguments of the function created by |
Having looked at the extract variable code when implementing #17587, I think the logic for this should probably be just added to the extract function assist.
I don't see any particular need to worry about analyzing whether this is capturing or not, the part that is necessary is just knowing whether the params of the closure match exactly those in the function (probably this needs to make sure they are in the same order and match the mutability / types). It's possible that I'm missing something here however - correct me if I'm wrong. |
If I understand that correctly that would fall short when the closure destructures arguments with patterns or when it ignores arguments e.g. with a wildcard pattern. I will add an example once I am at a PC and not on Mobile |
pairs // Vec<(i32, i32)>
.into_iter()
.filter(|(a, b)| a > b)
.collect::<Vec<_>>() should turn into pairs // Vec<(i32, i32)>
.into_iter()
.filter(predicate)
.collect::<Vec<_>>()
fn predicate((a, b): (i32, i32)) -> bool {
a > b
} but extract function would result in a signature mismatch as it would take in the tuple elements seperatly. pairs // Vec<(i32, i32)>
.into_iter()
.filter(|(a, b)| predicate(a, b))
.collect::<Vec<_>>()
fn predicate(a: i32, b: i32) -> bool {
a > b
} Similarly pairs // Vec<(i32, i32)>
.into_iter()
.filter(|(a, _)| a > 5)
.collect::<Vec<_>>() should turn into pairs // Vec<(i32, i32)>
.into_iter()
.filter(predicate)
.collect::<Vec<_>>()
fn predicate((a, _): (i32, i32)) -> bool {
a > 5
} but extract function would result in a signature mismatch as it would only take pairs // Vec<(i32, i32)>
.into_iter()
.filter(|(a, _)| predicate(a))
.collect::<Vec<_>>()
fn predicate(a: i32) -> bool {
a > 5
} The important part is that if the variables bound in the argument patterns matches, we want to re-use the closures argument declaration.
|
I think having a separate
|
The same thing could be said for non closures though, which suggests that this is a matter of determining the binding and not that its closure specific. let (a, b) = foo; fn((a, b): (u32, u33) { a + b } |
I don't think I would expect Resulting in let (a, b) = foo;
fun_name(a, b)
fn fun_name(a: i32, b: i32) -> i32 {
a + b
} and let (a, b) = foo;
fun_name(foo)
fn fun_name(foo: (i32, i32)) {
let (a, b) = foo;
a + b;
} respectively. Which already works like this today. |
Oh I didn't know about this issue but it is fixed by my PR #17940 (which also handles capturing closures, but not when the closure is passed into a function). |
Given the following code:
In VS Code, I want to select the predicate closure (
|x| x % 2 == 0
), click the yellow light bulb, and choose "Extract closure into function", and the code should turn into this:Note: The name
predicate
was deduced from the signature of thefilter
method. However, I wouldn't complain if rust-analyzer instead asks me for the name of the function.Note: Currently, an option called "Extract into function" exists, but it only moves a whole expression to a new function. In regard to closure, the new function it creates is a function that returns closure, not the closure itself.
The text was updated successfully, but these errors were encountered: