【问题标题】:Error: closure requires unique access to `self` but `self` is already borrowed错误:闭包需要对 `self` 的唯一访问,但 `self` 已被借用
【发布时间】:2020-06-22 23:10:24
【问题描述】:

我正在学习 Rust,并决定实现一块表(在 this pdf 的第 6.4 节中描述),因为它相当简单,但不平凡。

这主要是非常简单的,但我遇到了一个我无法弄清楚的问题。这是我的代码的简化版本,供参考:

use std::ops::Index;

#[derive(Debug)]
pub struct PieceTable {
    // original file data: never changes
    orig_buffer: Vec<u8>,
    // all new data is pushed onto this buffer
    add_buffer: Vec<u8>,
    // the pieces that currently make up the file
    pieces: Vec<Piece>,
}

#[derive(Debug, Copy, Clone)]
enum Location {
    Orig,
    Add,
}

#[derive(Debug, Copy, Clone)]
struct Piece {
    // which buffer is this piece located at?
    buf: Location,
    // starting offset
    start: usize,
    // size of piece
    length: usize,
}

impl PieceTable {
    pub fn iter(&self) -> PieceTableIterator {
        PieceTableIterator::new(self)
    }

    fn piece_buf(&self, piece: &Piece) -> &Vec<u8> {
        match piece.buf {
            Location::Orig => &self.orig_buffer,
            Location::Add => &self.add_buffer,
        }
    }

    fn piece_value(&self, piece: &Piece, index: usize) -> &u8 {
        &self.piece_buf(piece)[index]
    }
}

pub struct PieceTableIterator<'a> {
    table: &'a PieceTable,
    buf_iter: Option<std::slice::Iter<'a, u8>>,
    piece_iter: std::slice::Iter<'a, Piece>,
}

impl<'a> PieceTableIterator<'a> {
    fn new(table: &PieceTable) -> PieceTableIterator {
        let mut iter = table.pieces.iter();
        let piece = iter.next();
        let buf_iter = piece.map(|p| table.piece_buf(p).iter());
        PieceTableIterator {
            table: table,
            buf_iter: buf_iter,
            piece_iter: iter,
        }
    }
}

impl<'a> Iterator for PieceTableIterator<'a> {
    type Item = u8;

    fn next(&mut self) -> Option<u8> {
        if self.buf_iter.is_none() {

            return None;
        }
        match self.buf_iter {
            Some(ref mut iter) => {
                iter.next()
                    .or_else(|| {
                        self.piece_iter.next().and_then(|p| {
                            let mut buf = self.table.piece_buf(p)[p.start..(p.start + p.length)]
                                              .iter();
                            let item = buf.next();
                            self.buf_iter = Some(buf);
                            item
                        })
                    })
                    .map(|b| *b)
            }
            None => None,
        }
    }
}

fn main() {
    let table = PieceTable {
        orig_buffer: vec![1, 2, 3],
        add_buffer: vec![4, 5, 6],
        pieces: vec![Piece {
                         buf: Location::Orig,
                         start: 0,
                         length: 2,
                     },
                     Piece {
                         buf: Location::Add,
                         start: 0,
                         length: 3,
                     },
                     Piece {
                         buf: Location::Orig,
                         start: 2,
                         length: 1,
                     }],
    };
    // shoud print 1, 2, 4, 5, 6, 3
    for i in table.iter() {
        println!("{}", i);
    }

}

(Playground)

我正在尝试为这个结构构建一个迭代器。我可以通过在迭代器中保留一个索引来非常低效地做到这一点,但是对于每个.next() 调用,我必须遍历所有部分。相反,我宁愿让我的迭代器存储片段的迭代器和当前片段的缓冲区切片的迭代器。我的问题(我尝试了几种不同的方法)是我不断遇到终身问题。我当前的代码给了我错误:

:76:30: 84:22 error: closure requires unique access to `self` but `self.buf_iter.0` is already borrowed [E0500]

我想我明白了,但我不确定如何解决。我已经尝试了当前代码的一些变体,它们都遇到了类似的问题。

【问题讨论】:

    标签: rust


    【解决方案1】:

    就像错误信息告诉你的那样。问题是你有重叠的 self 可变借用。

        match self.buf_iter {
            Some(ref mut iter) => { /// self.buf_iter is mutably borrowed here
                iter.next()
                    .or_else(|| {
                        self.piece_iter.next().and_then(|p| { /// which is why you can't mutably borrow self here again
                            let mut buf = self.table.piece_buf(p)[p.start..(p.start + p.length)]
                                              .iter();
                            let item = buf.next();
                            self.buf_iter = Some(buf);
                            item
                        })
                    })
                    .map(|b| *b)
            }
            None => None,
        }
    

    解决方案是在第二个借用发生之前使第一个借用结束。

    还可以通过编写更惯用的 Rust 来稍微简化代码。

    impl<'a> Iterator for PieceTableIterator<'a> {
        type Item = u8;
    
        fn next(&mut self) -> Option<u8> {
            if self.buf_iter.is_none() {
                return None;
            }
    
            self.buf_iter
                .as_mut()
                .and_then(Iterator::next)
                .cloned()
                .or_else(|| {
                    self.piece_iter.next().and_then(|p| {
                        let mut buf = self.table.piece_buf(p)[p.start..(p.start + p.length)]
                                          .iter();
                        let item = buf.next().cloned();
                        self.buf_iter = Some(buf);
                        item
                    })
                })
        }
    }
    

    【讨论】:

      【解决方案2】:

      原始代码在现代 Rust 中按原样编译,这可能要归功于 non-lexical lifetimes 的更改。也可以进行一些小的惯用更改:

      #[derive(Debug)]
      pub struct PieceTable {
          // original file data: never changes
          orig_buffer: Vec<u8>,
          // all new data is pushed onto this buffer
          add_buffer: Vec<u8>,
          // the pieces that currently make up the file
          pieces: Vec<Piece>,
      }
      
      #[derive(Debug, Copy, Clone)]
      enum Location {
          Orig,
          Add,
      }
      
      #[derive(Debug, Copy, Clone)]
      struct Piece {
          // which buffer is this piece located at?
          buf: Location,
          // starting offset
          start: usize,
          // size of piece
          length: usize,
      }
      
      impl PieceTable {
          pub fn iter(&self) -> PieceTableIterator {
              PieceTableIterator::new(self)
          }
      
          fn piece_buf(&self, piece: &Piece) -> &Vec<u8> {
              match piece.buf {
                  Location::Orig => &self.orig_buffer,
                  Location::Add => &self.add_buffer,
              }
          }
      }
      
      pub struct PieceTableIterator<'a> {
          table: &'a PieceTable,
          buf_iter: Option<std::slice::Iter<'a, u8>>,
          piece_iter: std::slice::Iter<'a, Piece>,
      }
      
      impl<'a> PieceTableIterator<'a> {
          fn new(table: &PieceTable) -> PieceTableIterator {
              let mut piece_iter = table.pieces.iter();
              let piece = piece_iter.next();
              let buf_iter = piece.map(|p| table.piece_buf(p).iter());
              PieceTableIterator {
                  table,
                  buf_iter,
                  piece_iter,
              }
          }
      }
      
      impl<'a> Iterator for PieceTableIterator<'a> {
          type Item = u8;
      
          fn next(&mut self) -> Option<u8> {
              let iter = self.buf_iter.as_mut()?;
      
              iter.next()
                  .or_else(|| {
                      self.piece_iter.next().and_then(|p| {
                          let mut buf = self.table.piece_buf(p)[p.start..(p.start + p.length)].iter();
                          let item = buf.next();
                          self.buf_iter = Some(buf);
                          item
                      })
                  })
                  .copied()
          }
      }
      
      fn main() {
          let table = PieceTable {
              orig_buffer: vec![1, 2, 3],
              add_buffer: vec![4, 5, 6],
              pieces: vec![
                  Piece {
                      buf: Location::Orig,
                      start: 0,
                      length: 2,
                  },
                  Piece {
                      buf: Location::Add,
                      start: 0,
                      length: 3,
                  },
                  Piece {
                      buf: Location::Orig,
                      start: 2,
                      length: 1,
                  },
              ],
          };
          // shoud print 1, 2, 4, 5, 6, 3
          for i in table.iter() {
              println!("{}", i);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2015-07-02
        • 1970-01-01
        • 2017-01-26
        • 2019-12-03
        • 1970-01-01
        • 1970-01-01
        • 2014-08-09
        • 2017-08-10
        • 2018-10-11
        相关资源
        最近更新 更多