【问题标题】:Why does `while let Some(variable) = ...` not reassign `variable`?为什么`while let Some(variable) = ...`不会重新分配`variable`?
【发布时间】:2022-09-24 08:08:06
【问题描述】:

我试图在循环中爬上一条路径,直到满足路径上的某个条件。似乎一种有效的方法是使用while let 循环在每个循环上将循环变量重新分配给它的parent

let path = std::path::Path::new(\"/my/long/path/to/a/file.txt\");
while let Some(path) = path.parent() {
    println!(\"{:?}\", path);
    
    if path_matches_condition(&path) {
        println!(\"{:?} path matched\", path);
        break;
    }
}

但是,这会导致无限循环,因为path 没有在循环语句中重新分配。

我本来希望while let Some(path) = path.parent() 语句每次都重新分配path,但这不会发生,path.parent()path 不会改变。 即上述程序的输出将是\"/my/long/path/to/a\" 重复,直到程序被手动终止。

这可以通过分离两个变量并在循环内手动重新分配来解决。

let mut path = std::path::Path::new(\"/my/long/path/to/a/file.txt\");
while let Some(parent) = path.parent() {
    println!(\"{:?}\", path);
    
    if path_matches_condition(&path) {
        println!(\"{:?} path matched\", path);
        break;
    }

    path = parent;
}

发生这种情况是因为虽然path.parent()pathlet Some(path)path 名称相同,但它们是按范围分隔的?你能详细说明我的误解吗?有没有更惯用的方法来做这种事情?

  • 是的,Some(path) 中的path 遮蔽了外部path
  • 顺便说一句,我可能会做类似 path.ancestors().any(path_matches_condition) 的事情,而不是你的 while 循环。
  • @PitaJ,很好的建议。在我的实际用例中,我需要第一个匹配条件的祖先,所以我认为path.ancestors 方法不会顺利工作。我不确定ancestors的顺序是否得到保证,是否有一个好方法可以在第一次匹配后打破迭代。如果您有建议,我欢迎另一个答案。
  • 在这种情况下,path.ancestors().find(path_matches_condition) 将返回符合条件的第一个祖先。从该方法的文档来看,它似乎很清楚它从path 开始并向后迭代:path.parent()path.parent().parent()

标签: loops rust


【解决方案1】:

你所做的和the documentation 之间的显着区别在于,你确实从未重新分配path - 但我认为你误解了它没有被重新分配的地方。

这个说法:

let path = ...
while let Some(p) = path.parent() { ... }

接受path 并调用parent()Some(p) 中的 ppath 不是同一个变量。在您的代码中,当您输入 Some(path) 时,您正在遮蔽外部范围的 path 变量。但是一旦第一个循环结束,阴影就会消失,并且您实际上并没有重新分配 path 值,因此 path.parent() 返回与以前相同的值。

因此,您可能想要这个:

let path = (...)
let parent_path = path.parent()
while let Some(p) = parent_path { 
    ... 
    parent_path = p.parent();
}

请注意,出现阴影是因为 let Some(p) = path.parent() 正在执行解构。很像 match 语句 - 考虑以下代码:

fn main() {
    let path: Option<&str> = Some("A path");
    match path {
        Some(path: &str) => println!("{:#?}", path),
        None => println!("Nuthin!")
    }
    println!("{:#?}", path);
}

这编译得很干净,但我们可以清楚地看到第一个pathOption,而内部是&amp;str。输出是:

"A path"
Some(
    "A path",
)

【讨论】:

  • “但是一旦循环结束,阴影就会消失,这意味着你永远不会真正得到父母。”什么?他的第二个示例清楚地表明,您需要做的就是消除阴影(在Some 中绑定到不同的名称)以使其正常工作。
  • @PitaJ 澄清了这句话。是的,OP 所要做的就是消除阴影,尽管很明显 OP 不确定导致这种情况成立的机制。
猜你喜欢
  • 2017-04-12
  • 1970-01-01
  • 2016-11-02
  • 2018-05-25
  • 2019-11-16
  • 2011-09-25
  • 1970-01-01
  • 1970-01-01
  • 2020-11-07
相关资源
最近更新 更多