简体   繁体   中英

What are the Rust borrowing rules regarding mutable internal references?

It's not intuitive to me why a program like

#[derive(Debug)]
struct Test {
    buf: [u8; 16],
}

impl Test {
    fn new() -> Test {
        Test {
            buf: [0u8; 16],
        }       
    }   

    fn hi(&mut self) {
        self.buf[0] = 'H' as u8; 
        self.buf[1] = 'i' as u8; 
        self.buf[2] = '!' as u8; 
        self.print();
    }   

    fn print(&self) {
        println!("{:?}", self);
    }   
}

fn main() {
    Test::new().hi();
}

compiles and runs without any problem, but a program like

#[derive(Debug)]
enum State {
    Testing([u8; 16]),
}

#[derive(Debug)]
struct Test {
    state: State,
}       

impl Test {
    fn new() -> Test {
        Test {
            state: State::Testing([0u8; 16]),
        }
    }   

    fn hi(&mut self) {
        match self.state {
            State::Testing(ref mut buf) => {
                buf[0] = 'H' as u8;
                buf[1] = 'i' as u8;
                buf[2] = '!' as u8;
                self.print();
            },
        }
    }

    fn print(&self) {
        println!("{:?}", self);
    }
}

fn main() {
    Test::new().hi();
}

errors during compilation with an error of

error[E0502]: cannot borrow *self as immutable because self.state.0 is also borrowed as mutable

Since both programs do essentially the same thing, the second doesn't seem like it would be somehow more unsafe from a memory perspective. I know there must be something about the borrowing and scoping rules that I must be missing, but have no idea what.

In order to make your hi function work you just need to move print out of the scope of the mutable borrow introduced in its match expression:

fn hi(&mut self) {
    match self.state {
        State::Testing(ref mut buf) => {
            buf[0] = 'H' as u8;
            buf[1] = 'i' as u8;
            buf[2] = '!' as u8;
        },
    }
    self.print();
}

Your two variants are not equivalent due to the presence of the match block in the second case. I don't know how to directly access the tuple struct in the enum without pattern matching (or if this is even possible right now), but if it was the case, then there would in fact be not much difference and both versions would work.

In the match statement, you borrow self.state . Borrow scopes are lexical, so it is borrowed in the entire match block. When you call self.print() , you need to borrow self . But that is not possible, because part of self is already borrowed. If you move self.print() after the match statement, it will work.

Regarding the lexical borrow scope, you can read more in the second part of Two bugs in the borrow checker every Rust developer should know about . Related issues: #6393 , #811 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM