Skip to content

Inconsistent auto-reborrowing behavior of generic functions #65279

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

Closed
tcbbd opened this issue Oct 10, 2019 · 5 comments
Closed

Inconsistent auto-reborrowing behavior of generic functions #65279

tcbbd opened this issue Oct 10, 2019 · 5 comments

Comments

@tcbbd
Copy link

tcbbd commented Oct 10, 2019

Look at this piece of code:

fn f<T>(x: T, y: T) {}

fn main() {
    let x = &mut 0i32;
    let y = &mut 1i32;
    f(x, y);
    let z = &mut *y;
    println!("{} {} {}", x, y, z);
}

From the internet, I get the impression that generic functions will not auto-reborrow if the type of parameter cannot be determined to be a mutable reference before instantiation.

It works as expected for x:

error[E0382]: borrow of moved value: `x`
  --> src/main.rs:21:26
   |
17 |     let x = &mut 0i32;
   |         - move occurs because `x` has type `&mut i32`, which does not implement the `Copy` trait
18 |     let y = &mut 1i32;
19 |     f(x, y);
   |       - value moved here
20 |     let z = &mut *y;
21 |     println!("{} {} {}", x, y, z);
   |                          ^ value borrowed here after move

But does auto-reborrow for y:

error[E0502]: cannot borrow `y` as immutable because it is also borrowed as mutable
  --> src/main.rs:21:29
   |
20 |     let z = &mut *y;
   |             ------- mutable borrow occurs here
21 |     println!("{} {} {}", x, y, z);
   |                             ^  - mutable borrow later used here
   |                             |
   |                             immutable borrow occurs here

Now this is really confusing. What's the exact rules for auto-reborrow? When does it happen, before or after type inference?

@hellow554
Copy link
Contributor

I think this question is better suited for the rust user forum at https://users.rust-lang.org

I can't see, that this is a bug, but hey: prove me wrong ;)

@tcbbd
Copy link
Author

tcbbd commented Oct 11, 2019

@hellow554 Maybe you're right in that this is not a bug, but anyway I think there's some space for improvements. The current behavior seems suboptimal to me, and maybe we can make it behave more consistently?

@tcbbd
Copy link
Author

tcbbd commented Oct 12, 2019

Now I think this is related to #35919. In f(x, y), we need to infer the type of T using x, so x is simply moved. But T is known after that, so y is reborrowed instead of being moved, this is exactly what happened in #35919.

Here's another interesting example, which can also be confusing for new comers:

struct Struct<T> {
    s: T,
    t: T,
}

let x = &mut 0i32;
let y = &mut 1i32;

let u = Struct{ s: x, t: y }; // x is moved, y is reborrowed
let v = Struct{ t: y, s: x }; // y is moved, x is reborrowed

Maybe we should add some clarification to our doc?

@Mark-Simulacrum
Copy link
Member

Unfortunately I don't think this is really actionable -- this is certainly a subtle area of the language but I do not believe we can make any real improvements to documentation here given how "in the weeds" this is. I'm going to close to that effect, especially since #35919 as you mention covers similar ground (so, in some sense, though not a perfect one, this is a close-to-duplicate).

@ssh352
Copy link

ssh352 commented Apr 6, 2023

I understand the explanation but this is really confusing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants