Skip to content
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

How to correct "prior loan as mutable" error with subsequent method calls? #5829

Closed
cdleary opened this issue Apr 10, 2013 · 3 comments
Closed

Comments

@cdleary
Copy link

cdleary commented Apr 10, 2013

Using rust 0.6 from homebrew. Trying to do two subsequent method calls with a mutable borrowed self -- by contrast, any number of calls to "peek" seem to work okay:

struct Numberizer <'self> {
    numbers: &'self[int],
    current: uint
}

impl <'self> Numberizer <'self> {
    fn peek(&self) -> int {
        self.numbers[self.current]
    }
    fn pop(&mut self) -> int {
        let rv = self.peek();
        self.current += 1;
        rv
    }
}

fn main() {
    let numbers: ~[int] = ~[1, 42, 3, 4, 5];
    let mut o = Numberizer {numbers: numbers, current: 0};
    io::println(fmt!("%d", o.pop())); // note: prior loan as mutable granted here
    io::println(fmt!("%d", o.pop())); // error: loan of mutable local variable as mutable conflicts with prior loan
}

(I may be doing it wrong but I figured making a github issue would at least make the fix searchable. :-)

$ rustc --version
rustc 0.6
host: x86_64-apple-darwin
@alexcrichton
Copy link
Member

I've run into a lot of similar errors, and the common case which I've seen is that the compiler really doesn't like borrowed pointers in structs with mutable methods. Even something as simple as below fails to compile:

struct Foo<'self> {
  a: &'self int
}

impl<'self> Foo<'self> {
  fn foo(&mut self) {}
}

fn main() {
  let n = 1;
  let mut foo = Foo { a: &n };
  foo.foo();
  foo.foo();
}

I'm not sure if this is an actual error or some bug in the compiler, but I do agree that the error message could probably be more helpful.

@stevenblenkinsop
Copy link

This is correct. The problem is that the lifetime of the borrow is the same as a lifetime that escapes the function call, in this case via a mutable field, and therefore it persists for the lifetime of the mutable field. Consider this code:

struct T<'self> {
    x: &'self mut int,
    y: int,
}

fn F1<'a>(t: &'a mut T<'a>) {
    *&mut t.x = &mut t.y;
}

fn F2<'a, 'b>(_t: &'a mut T<'b>) {
}

fn main() {
    let mut x = 5;
    let mut t = T{x: &mut x, y: 6};
    F2(&mut t);
    F2(&mut t);
}

In F1, I'm able to move my mutable borrow of t into t.x so that it persists for the lifetime of t.x, which is the entire body of main. Therefore, no other borrows of t can coexist with an F1(&mut t); call. However, in F2, because t and t.x have different lifetimes, my mutable borrow of t can't escape, so multiple F2(&mut t); calls are okay.

Unfortunately, this trick doesn't quite work yet in rust-0.6 when it comes to methods because of how the lifetime of the self parameter is handled. This is issue #5656.

@thestinger
Copy link
Contributor

This is fixed thanks to all the work @nikomatsakis did on making the self parameter use an explicit region.

flip1995 pushed a commit to flip1995/rust that referenced this issue Jul 26, 2020
Use `(std::)f64::EPSILON` in the examples as suggested in the lints

`float_cmp(_const)` suggests using `{f32|f64}::EPSILON` and it'd be great if the docs mentioned it.

changelog: none
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