-
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
Type inference of lambda arguments interferes with borrow checking #62640
Comments
I had a similar issue too.
However, if I leave
I dug into Mir of both code snippet, and found out that the type of
|
I think that is because when you specify the type, reborrow occurs. When you don't specify the type, the mutable reference will be moved into the function. |
@Hyuuko As far as I know, when calling functions or closures, the default behavior is to reborrow references into parameters. If the reference had been moved, it seems like a bug or a rare behavior to me. Is there any document addressing this move behavior? |
Yeah, I just got bit by this in a very similar example, and I'm a bit disturbed because if this behavior is expected and type annotation is required in some closures, then I need to change some language in TRPL, like:
|
Even stranger, type inference affects how borrows are treated inside a closure. This code fails to compile: fn main() {
fn consumer(var: &mut i32) {
*var += 1;
}
let closure = |var| {
consumer(var);
consumer(var);
};
let mut x: i32 = 0;
closure(&mut x);
} Output:
Annotating This kind of cripples type inference for mutable references. |
This happens even when the closure is never used, the problem seems to be that when the type of |
I was playing around to see if this issue still reproed and crafted a more minimal example (playground link): struct Bar {}
fn foo() -> Bar { Bar {} }
fn main() {
let test = |_t| {};
test(&foo());
test(&foo());
} The compiler error is slightly different, but I'm pretty sure the underlying issue is the same
As before annotating the lambda type to be |
For anyone trying to understand why this happens, I found #100002 a year ago when I encountered this strange behavior myself. The "But Why?" section explain the underlying compiler decision that causes this problem. If I understand it correctly, it says that when the argument of a closure contains reference but is not type-annotated, the lifetime of this reference would be a lifetime parameter of the closure and thus must be as long as the lifetime of the closure, when it is type-annotated, the lifetime is higher-ranked, i.e. it can be any lifetime and is determined every time the closure is called. let mut x = 42;
let f = |x| {}; // ┬
f(&x); // │ the lifetime of f
&mut x; // ┐ │
f; // │ ┴
// Err: can not borrow as mutable as it is also borrowed immutably Stabilization of #97362 is probably going to mitigate this rather "dumb" rule. |
Another slightly different repro: fn g<T, F: Fn(T)>(_: T, _: F) {}
fn main() {
let f = |_| ();
// let f = |_: &_| (); // <-- this works
for i in 1..1 {
g(&vec![i], f);
}
} It certainly took me a while to find the culprit in my significantly more complex code… |
It looks like type inference of arguments to a lambda function somehow interferes with borrow checking.
I tried this code: (playground link)
I expected to see this happen:
A successful build
Instead, this happened:
Compilation failed with this error:
Note that changing line 20 to
results in a successful compilation (playground link)
Meta
rustc --version --verbose
:The text was updated successfully, but these errors were encountered: