如何重试会改变某些状态的操作?

I'm trying to rewrite the less pager using mmap. I'm trying to represent the different states as a function that transforms an iterator of commands into iterator of draw commands which are executed by a trait Screen

完整的代码可以在这里找到。

https://github.com/minnimumm/morr/tree/temp

struct NormalMode<'a> {
    line_reader: &'a mut LineReader<'a>,
    current_range: LinesRange,
}
impl<'a> NormalMode<'a> {

    fn read<'b>(&'b mut self, r: &LinesRange) -> ReadLines<'b> {
        self.line_reader.read(r)
    }

    fn f(
        &'a mut self,
        vmove: VerticalMove,
        rows: usize
    ) -> Option<DrawCommand<'a>>
    {
        let new_range = mv(vmove, self.current_range.clone(), rows);
        let requested_nr = new_range.range.size_hint().0;
        let maybe_lines = {
            let lines = self.read(&new_range);
            match requested_nr - lines.lines.len() {
                0 => Ok(lines),
                n => Err(n),
            }
        };
        return match maybe_lines {
            Ok(lines) => Some(DrawCommand::DrawContent { lines: lines } ),
            Err(n) => {
                let lines = self.read(&new_range.extendl(n));
                if lines.range != self.current_range {
                    Some(DrawCommand::DrawContent { lines: lines} )
                } else {
                    None
                }
            }
        }
    }
}

这是一些错误

||    |
|| 50 | impl<'a> NormalMode<'a> {
||    |      -- lifetime `'a` defined here
|| ...
|| 65 |             let lines = self.read(&new_range);
||    |                         ---- first mutable borrow occurs here
|| ...
|| 72 |             Ok(lines) => Some(DrawCommand::DrawContent { lines: lines } ),
||    |                          ------------------------------------------------ returning this value requires that `*self` is borrowed for `'a`
|| 73 |             Err(n) => {
|| 74 |                 let lines = self.read(&new_range.extendl(n));
||    |                             ^^^^ second mutable borrow occurs here
src/main.rs|75 col 35 error 502| cannot borrow `self.current_range` as immutable because it is also borrowed as mutable
||    |
|| 50 | impl<'a> NormalMode<'a> {
||    |      -- lifetime `'a` defined here
|| ...
|| 65 |             let lines = self.read(&new_range);
||    |                         ---- mutable borrow occurs here
|| ...
|| 72 |             Ok(lines) => Some(DrawCommand::DrawContent { lines: lines } ),
||    |                          ------------------------------------------------ returning this value requires that `*self` is borrowed for `'a`
|| ...
|| 75 |                 if lines.range != self.current_range {
||    |                                   ^^^^^^^^^^^^^^^^^^ immutable borrow occurs here
|| Some errors have detailed explanations: E0499, E0502.
|| For more information about an error, try `rustc --explain E0499`.

我理解借款检查程序为何抱怨-在同一生命周期中有两个可变借款,但是我不明白该代码中可能出什么问题。

为什么这样做是错误的?我该如何克服这个问题?如何重试会改变某些状态的操作?

评论
  • [呛人心]
    [呛人心] 回复

    It is true that nothing can go wrong with your code, but you know that because you know what Result::or_else() does: it calls your lambda only if it is an Err, but never if it is an Ok. So the fact that the Ok variant lifetime overlaps with the lifetime of your lambda is not an issue.

    但是Rust编译器不是那么聪明,它有多聪明的规则是所有借位检查规则必须仅在当前函数以及所用函数和类型的声明方面是可执行的,但绝不应该依赖于代码其他功能。

    So the correctness of your function cannot depend on what Result::or_else() does. What would happen if it accessed to the contained value in case of an Ok? You would have a problem!

    The solution is actually quite easy: just write a match:

    let lines = match maybe_lines {
        Ok(s) => s,
        Err(n) => self.read(&new_range.extendl(n),
    };
    

    This way the borrow-checker is happy, because your call to self.read and s have different scopes.