关于 Rust 可变内部引用的借用规则是什么?

4

对我来说,像这样的程序为什么会这样并不直观。

#[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();
}

编译和运行都没有问题,但是像这样的程序
#[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();
}

编译时遇到错误,错误信息为

error[E0502]: 因为 self.state.0 被作为可变引用借用了,所以无法将 *self 借用为不可变引用

由于这两个程序本质上做相同的事情,第二个程序看起来并不会从内存方面更不安全。我知道一定有关于借用和作用域规则的东西我没有掌握,但是不知道是什么。

2个回答

3
为使您的hi函数工作,您只需将print移出可变借用所引入的match表达式的范围:
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();
}

由于第二种情况中存在match块,因此您的两种变量不相等。我不知道如何在没有模式匹配的情况下直接访问enum中的元组结构(或者这是否当前可能),但如果是这种情况,则实际上两个版本之间并没有太大的区别,两个版本都可以工作。


你可以使用 if let 进行模式匹配,而不是在这里使用 match,尽管我认为 match 更具有“未来可靠性”。 - Matthieu M.
为什么match更具未来性?“if let”不稳定吗? - avl_sweden
1
@avl_sweden 我的意思是,随着项目的扩展,State 枚举可能会有更多的变体。 - ljedrz

2
match语句中,你借用了self.state。借用作用域是词法的,因此它在整个match块中被借用。当你调用self.print()时,你需要借用self。但这是不可能的,因为self的一部分已经被借用了。如果你将self.print()移动到match语句之后,它就会起作用。
关于词法借用作用域,您可以在每个Rust开发人员都应该知道的借用检查器中的两个错误的第二部分中阅读更多信息。相关问题:#6393#811

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接