Rust中的结构上的惯用迭代器+变异?

时间:2014-09-21 14:37:20

标签: rust

我刚刚写完这段代码:

/// Watcher to look for changes in files
pub struct Watch<'a> {
  glob:Option<Glob>,
  ruv:Vec<*mut c_void>,
  paths:Vec<Path>,
  pub callback:Option<|Path, WatchEvent|: 'a>
}

impl<'a> Watch<'a> {

  /// Start watching for changes in any of the files matching known patterns
  pub fn watch(&mut self, path:&str) {
    let g = self.glob.take().unwrap();
    for path in g.iter(path) {
      match self.add_unique(&path) {
        Some(p) => { self.bind(&path); },
        None => {}
      }
    }
    self.glob = Some(g);
  }

  /// Check for a matching path
  fn add_unique(&mut self, path:&Path) -> Option<Path> {
    let mut found = false;
    for i in self.paths.iter() {
      if i.as_str().unwrap().as_bytes() == path.dirname() {
        found = true;
        break;
      }
    }
    if !found {
      self.paths.push(Path::new(path.dirname()));
      return Some(Path::new(path.dirname()));
    }
    return None;
  }

...

}

我特别不满意不得不将Glo移出Watch实例,以便我可以迭代路径匹配并更新对象的状态。

我尝试了一些方法,但所有其他选择涉及创建临时向量,这是完全不可行的(glob可能匹配数百个文件,但我只关心文件树中的唯一路径,以便它们可以是绑定到libuv的文件事件api;我绝对想要创建一个包含每个匹配文件的巨大列表的向量。)

怎么能用更惯用的形式写出来?

我觉得使用函数调用可以大大简化它,但它的确切语义使我无法理解。

实质上我在这里想要实现的是:

for every filename in g.iter(path)
  if the path of the filename is not known yet
    call bind() on the path
    save the filename so future calls to watch() dont duplicate watch it

这是一个带有虚拟数据的游戏围栏,显示了它的实际效果:http://is.gd/09wYTS

编辑:这就是我必须将Glob移到iter()上的原因:http://is.gd/FGlmw5

1 个答案:

答案 0 :(得分:2)

正如我所看到的,这是迭代器失效的典型问题。您正在尝试修改正在迭代的结构。当用C ++编写时,它会编译,但可能会发生“神奇”的事情。

处理此类情况的惯用方法是为要插入的项创建临时结构,然后将它们插入for循环之外。

有些事情:

pub fn watch(&mut self, path:&str) {
  let temp = Vec::new();
  for path in self.glob.iter(path) {
    match self.would_add_unique(&path) {
      Some(p) => { self.bind(&p); temp.push_back(&p); },
      None => {}
    }
  }

  for item in temp.iter() {
    self.add_unique(&item);
  }
}

或者您可以重新设计您的结构,使它们不会作为单一值。