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

Different borrow checker behavior with externally similar code #18961

Closed
netvl opened this issue Nov 14, 2014 · 6 comments
Closed

Different borrow checker behavior with externally similar code #18961

netvl opened this issue Nov 14, 2014 · 6 comments
Labels
A-borrow-checker Area: The borrow checker

Comments

@netvl
Copy link
Contributor

netvl commented Nov 14, 2014

Consider this piece of code:

struct S1<T> { s: Option<T> }
//struct S1<T> { s: *mut T }

struct Foo<'a> {
    bar: S1<&'a str>
}

impl<'a> Foo<'a> {
    pub fn new() -> Foo<'a> {  // '
        Foo { bar: S1 { s: None } }
        //Foo { bar: S1 { s: std::ptr::null_mut() } }
    }

    pub fn baz(&'a self) -> Option<int> {
        None
    }

    pub fn qux(&'a mut self, retry: bool) {
        let opt = self.baz();
        if retry { self.qux(false); }
    }
}

pub fn main() {
   let mut foo = Foo::new();
   foo.qux(true);
}

It compiles. However, if you change S1 definition to the commented one:

//struct S1<T> { s: Option<T> }
struct S1<T> { s: *mut T }

struct Foo<'a> {
    bar: S1<&'a str>
}

impl<'a> Foo<'a> {
    pub fn new() -> Foo<'a> {  // '
        //Foo { bar: S1 { s: None } }
        Foo { bar: S1 { s: std::ptr::null_mut() } }
    }

    pub fn baz(&'a self) -> Option<int> {
        None
    }

    pub fn qux(&'a mut self, retry: bool) {
        let opt = self.baz();
        if retry { self.qux(false); }
    }
}

pub fn main() {
   let mut foo = Foo::new();
   foo.qux(true);
}

it stops compiling with borrow checking error (and very weird one):

<anon>:20:20: 20:24 error: cannot borrow `*self` as mutable because it is also borrowed as immutable
<anon>:20         if retry { self.qux(false); }
                             ^~~~
<anon>:19:19: 19:23 note: previous borrow of `*self` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `*self` until the borrow ends
<anon>:19         let opt = self.baz();
                            ^~~~
<anon>:21:6: 21:6 note: previous borrow ends here
<anon>:18     pub fn qux(&'a mut self, retry: bool) {
<anon>:19         let opt = self.baz();
<anon>:20         if retry { self.qux(false); }
<anon>:21     }
              ^
error: aborting due to previous error

Originally discovered in this Stackoverflow question. It is much more confusing there because Vec and HashMap instead of custom structure are used.

cc @nikomatsakis (right?)

@jgillich
Copy link

As noted in the SO comments, the second snippet builds when you remove the lifetime annotation on baz.

@nikomatsakis
Copy link
Contributor

This is #3598. The problem is variance.

@nikomatsakis
Copy link
Contributor

Hmm, actually, I could be mistaken. Let me read a bit more.

@nikomatsakis
Copy link
Contributor

I take it back, I am not sure what's going on here just yet, I'll have to dig in.

@steveklabnik steveklabnik added the A-borrow-checker Area: The borrow checker label Jan 29, 2015
@steveklabnik
Copy link
Member

Updated code:

//struct S1<T> { s: Option<T> }
struct S1<T> { s: *mut T }

struct Foo<'a> {
    bar: S1<&'a str>
}

impl<'a> Foo<'a> {
    pub fn new() -> Foo<'a> {  // '
        //Foo { bar: S1 { s: None } }
        Foo { bar: S1 { s: std::ptr::null_mut() } }
    }

    pub fn baz(&'a self) -> Option<isize> {
        None
    }

    pub fn qux(&'a mut self, retry: bool) {
        let opt = self.baz();
        if retry { self.qux(false); }
    }
}

pub fn main() {
   let mut foo = Foo::new();
   foo.qux(true);
}

Same error today.

@Mark-Simulacrum
Copy link
Member

I don't think this is actually a bug. I minimized the code, and qux borrows the struct for its entire lifetime, so it is logical that you cannot call it twice; if we remove the 'a annotation on qux's &mut self, then everything compiles just fine--as expected.

I'm going to close this, but please do reopen if it's actually a bug, and if possible provide at least a quick explanation as to why.

use std::marker::PhantomData;

struct Foo<'a>(PhantomData<&'a i32>);

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

fn main() {
   let mut foo = Foo(PhantomData);
   foo.qux();
   foo.qux();
}

lnicola pushed a commit to lnicola/rust that referenced this issue Jan 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-borrow-checker Area: The borrow checker
Projects
None yet
Development

No branches or pull requests

5 participants