rust - 从子结构中获取值,然后修改子结构 : a borrow checker puzzle?

标签 rust borrow-checker

考虑这个玩具代码:

struct X {
    x: usize,
    times_accessed: usize,
}

impl X {
    fn get(&mut self) -> usize {
        self.times_accessed = self.times_accessed + 1;
        self.x
    }
}

struct Y {
    y: usize,
    max_access: usize,
    option_x: Option<X>,
}

impl Y {
    fn get_x(&mut self) -> Option<usize> {
        match self.option_x {
            Some(ref mut some_x) => {
                let result = some_x.get();

                if some_x.times_accessed == self.max_access {
                    self.option_x = None;
                }

                Some(result)
            }
            None => {
                println!("option_x is not initialized! try calling Y::init_x");
                None
            }
        }
    }

    fn init_x(&mut self, x_val: usize, max_allowed_access: usize) {
        self.max_access = max_allowed_access;
        self.option_x = Some(X {
            x: x_val,
            times_accessed: 0,
        });
    }
}

fn main() {
    println!("hello world!");
}

我没有在 main 函数中使用 Y,因为编译器不需要我这样做来指出借用检查器不会对 的实现感到满意>Y::get_x:

error[E0506]: cannot assign to `self.option_x` because it is borrowed
  --> src/main.rs:26:21
   |
22 |             Some(ref mut some_x) => {
   |                  -------------- borrow of `self.option_x` occurs here
...
26 |                     self.option_x = None;
   |                     ^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.option_x` occurs here

我从借用检查员的角度理解这个问题,我可以想出一个非常简单的解决方法:将 some_x 中的值复制到 result 中(我不是已经这样做,毕竟 result 不是引用,并且 usize 具有 Copy 特性),然后“删除引用”到 some_x(是否会使用 drop(some_x);,以便我可以修改 option_x?此处提供了一个版本,但仍然不起作用:

fn get_x(&mut self) -> Option<usize> {
    match self.option_x {
        Some(ref mut some_x) => {
            let result = some_x.get();

            if some_x.times_accessed == self.max_access {
                drop(some_x);
                self.option_x = None;
            }

            Some(result)
        }
        None => {
            println!("option_x is not initialized! try calling Y::init_x");
            None
        }
    }
}

应该怎么办?

最佳答案

如错误消息所述,您在尝试替换 self.option_x 时引用其中的内容:

Some(ref mut some_x) => {
    let result = some_x.get();

    if some_x.times_accessed == self.max_access {
        self.option_x = None; // <---------
    }

    Some(result)
}

访问 some_x 的突出显示点之后的任何代码都将获得无效值。这就是程序崩溃、暴露安全漏洞等的原因。

在 Rust 的潜在未来,理解非词法生命周期 的编译器可能会意识到您不使用该值,因此代码在当前状态下是可以的。在那之前,您可以从 Option 中取出整个值,如果不满足您的条件,则将其放回去:

match self.option_x.take() {
    Some(mut some_x) => {
        let result = some_x.get();

        if some_x.times_accessed != self.max_access {
            self.option_x = Some(some_x);
        }

        Some(result)
    }

另见:

关于rust - 从子结构中获取值,然后修改子结构 : a borrow checker puzzle?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47623432/

相关文章:

multithreading - Rayon 会避免为少量工作生成线程吗?

rust - 哪些 Rust 1.2 容器支持特征对象?

generics - 为什么移动闭包不会捕获具有通用类型的值?

rust - 嵌套数组索引中的 "cannot borrow as immutable because it is also borrowed as mutable"是什么意思?

rust - 我如何借用选项中的项目或在没有时创建新项目?

callback - 如何实现 JavaScript 风格的回调?

rust - 在向量中调用结构的特征方法时出现问题

rust - PNaCl 对 Rust 的支持

rust - 为什么 "the temporary is part of an expression at the end of a block"是一个错误?

rust - 如何在 Rust 中实现获取缓存或加载操作?