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

mut borrow checker seems to be too conservative in matches #10390

Closed
erickt opened this issue Nov 9, 2013 · 7 comments
Closed

mut borrow checker seems to be too conservative in matches #10390

erickt opened this issue Nov 9, 2013 · 7 comments
Labels
A-borrow-checker Area: The borrow checker

Comments

@erickt
Copy link
Contributor

erickt commented Nov 9, 2013

I've been working on a new version of the serialization framework, and I've run into an edge case with the borrow checker that is a little painful. Here's my reduced code:

pub enum Value<'self> {
    A(&'self str),
    B,
    C,
}

pub trait Decoder<'self> {
    fn read<'a>(&'a mut self) -> Value<'a>;
}

pub trait Decodable<'self, D: Decoder<'self>> {
    fn decode(d: &mut D) -> Self;
}

impl<'self, D: Decoder<'self>> Decodable<'self, D> for () {
    fn decode(d: &mut D) -> () {
        match d.read() {
            A(x) => { let _ = x; () }
            B => Decodable::decode(d),
            C => Decodable::decode(d),
        }
    }
}

This errors with:

foo.rs:19:35: 19:36 error: cannot borrow `*d` as mutable more than once at a time
foo.rs:19             B => Decodable::decode(d),
                                             ^
foo.rs:17:14: 17:15 note: second borrow of `*d` as mutable occurs here
foo.rs:17         match d.read() {
                        ^
foo.rs:20:35: 20:36 error: cannot borrow `*d` as mutable more than once at a time
foo.rs:20             C => Decodable::decode(d),
                                             ^
foo.rs:17:14: 17:15 note: second borrow of `*d` as mutable occurs here
foo.rs:17         match d.read() {
                        ^
error: aborting due to 2 previous errors

I'm not sure if this should be an error because the error is being caused by the A variant capturing the value, not the B and C variant. This is clear with this impl, which compiles fine:

impl<'self, D: Decoder<'self>> Decodable<'self, D> for () {
    fn decode(d: &mut D) -> () {
        match d.read() {
            A(*) => (),
            B => Decodable::decode(d),
            C => Decodable::decode(d),
        }
    }
}

The only way currently that I can get this to work is if I use a mini-state machine, as in:

impl<'self, D: Decoder<'self>> Decodable<'self, D> for () {
    fn decode(d: &mut D) -> () {
        enum State {
            BState,
            CState,
        }

        let state = match d.read() {
            A(x) => { let _ = x; return (); }
            B => BState,
            C => CState,
        };

        match state {
            BState => Decodable::decode(d),
            CState => Decodable::decode(d),
        }
    }
}

Which is a bit obnoxious to write when we have a large amount of value variants.

@nikomatsakis
Copy link
Contributor

Dup of #6393 -- fixing this is not so easy

@erickt
Copy link
Contributor Author

erickt commented Nov 12, 2013

@nikomatsakis: You mentioned on IRC this might not actually get fixed once #6393 is implemented. Should this be reopened?

@nikomatsakis
Copy link
Contributor

We can reopen, but we have to be more precise what you are asking for. This is kind of a subtle problem with many dimensions. I think the problem, specifically, is that returning Value<'a> says that the lifetime of the returned value is linked to the lifetime of the &mut self parameter, right? But this isn't really what you wanted, because it implies that you cannot use the self object so long as the Value is live.

If I had to guess, I'd guess that what you wanted is more like "this Value object will be freed when self goes away, but they are otherwise independent". Does that sound right?

I have encountered a similar scenario with the Arena type (described in #10444). The basic fix that I want to do there is to move the lifetime in the result into a parameter on the Arena type ('pool in that case). This is more or self what you have with 'self -- currently 'self is not used in this trait, so I don't really know what it's supposed to represent, I would have thought it represents the lifetime of the returned value. Or is it just a holdover?

@nikomatsakis nikomatsakis reopened this Nov 12, 2013
@pnkfelix
Copy link
Member

cc me

@steveklabnik
Copy link
Member

Updated code:

pub enum Value<'a> {
    A(&'a str),
    B,
    C,
}

pub trait Decoder {
    fn read<'a>(&'a mut self) -> Value<'a>;
}

pub trait Decodable<D: Decoder> {
    fn decode(d: &mut D) -> Self;
}

impl<D: Decoder> Decodable<D> for () {
    fn decode(d: &mut D) -> () {
        match d.read() {
            Value::A(x) => { let _ = x; () }
            Value::B => Decodable::decode(d),
            Value::C => Decodable::decode(d),
        }
    }
}

fn main() {}

Is this ticket still worthwhile? I would agree with what @nikomatsakis is saying, with regards to the lifetime here.

@steveklabnik
Copy link
Member

(Also, I'm not sure what to tag this as)

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

I am inclined to close this ticket. There are various ways we could improve the borrow checker and I don't know that having this ticket open will help us. It's not a bug in any case, more like an enhancement.

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

4 participants