【问题标题】:Rust matching and borrow checkerRust 匹配和借用检查器
【发布时间】:2018-01-12 07:43:24
【问题描述】:

我一直在我的 Rust 程序中偶然发现一个模式,这总是让我与借用检查器发生冲突。考虑以下玩具示例:

use std::sync::{Arc,RwLock};

pub struct Test {
    thing: i32,
}

pub struct Test2 {
    pub test: Arc<RwLock<Test>>,
    pub those: i32,
}

impl Test {
    pub fn foo(&self) -> Option<i32> {
        Some(3)
    }
}

impl Test2 {
    pub fn bar(&mut self) {
        let mut test_writer = self.test.write().unwrap();

        match test_writer.foo() {
            Some(thing) => {
                self.add(thing);
            },
            None => {}
        }
    }

    pub fn add(&mut self, addme: i32) {
        self.those += addme;
    }
}

这不会编译,因为 Some 分支中的 add 函数试图可变地借用 self,它已经在 match 语句上方不可变地借用以打开读写锁。

我在 Rust 中遇到过几次这种模式,主要是在使用 RwLock 时。我还找到了一种解决方法,即在 match 语句之前引入一个布尔值,然后更改 Some 臂中布尔值的值,然后最后在 match 语句之后对该布尔值引入一个测试以执行任何操作是我想在Some 手臂上做的。

在我看来这不是解决问题的方法,我认为在 Rust 中有一种更惯用的方法来做到这一点 - 或者以完全不同的方式解决问题 - 但我找不到它。如果我没记错的话,这个问题与词法借用有关,所以self 不能在匹配语句的范围内可变地借用。

有没有一种惯用的 Rust 方法来解决这类问题?

【问题讨论】:

    标签: rust matching borrow-checker


    【解决方案1】:

    直接使用字段those,例如自定义类型:

    use std::sync::{Arc,RwLock};
    
    pub struct Those(i32);
    
    impl Those {
        fn get(&self) -> i32 {
            self.0
        }
    
        fn add(&mut self, n: i32) {
            self.0 += n;
        }
    }
    
    pub struct Test {
        thing: Those,
    }
    
    pub struct Test2 {
        pub test: Arc<RwLock<Test>>,
        pub those: Those,
    }
    
    impl Test {
        pub fn foo(&self) -> Option<Those> {
            Some(Those(3))
        }
    }
    
    impl Test2 {
        pub fn bar(&mut self) {
            let mut test_writer = self.test.write().unwrap();
    
            match test_writer.foo() {
                Some(thing) => {
                    // call a method add directly on your type to get around the borrow checker
                    self.those.add(thing.get());
                },
                None => {}
            }
        }
    }
    

    【讨论】:

    • 这是惯用的解决方案。
    【解决方案2】:

    你要么需要结束对self的一部分的借用,然后再改变self

    pub fn bar1(&mut self) {
        let foo = self.test.write().unwrap().foo();
        match foo {
            Some(thing) => {
                self.add(thing);
            },
            None => {}
        }
    }
    

    或直接变异self的非借用部分

    pub fn bar2(&mut self) {
        let test_writer = self.test.write().unwrap();
    
        match test_writer.foo() {
            Some(thing) => {
                self.those += thing;
            },
            None => {}
        }
    }
    

    【讨论】:

    • 感谢 red75prime。我将接受 Boiethios 的答案作为正确答案,因为在我看来,他回答了“惯用”部分。但在我的代码中,我将使用您的第一个建议的变体 =)
    • @Vincent 您必须使用的解决方案取决于代码的复杂性。如果真的很复杂,尽量减少不需要的借用次数;否则你迟早会和借用检查器作斗争。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-11
    • 2014-09-10
    • 1970-01-01
    相关资源
    最近更新 更多