在闭包和组合器中,当存在借用时,值会过早地被丢弃

4

我遇到了一个问题,就是在闭包中的 Option 内部仍然被借用时,一个值被丢弃了,但我很难完全理解发生了什么。为了说明这一点,以下是一个实际尝试实现的工作示例:

fn foo() -> Option<String> {
    let hd = match std::env::home_dir() {                                       
        Some(d) => d,                                                           
        None => return None,                                                    
    };                                                                          
    let fi = match hd.file_name() {                                             
        Some(f) => f,                                                           
        None => return None,                                                    
    };                                                                          
    let st = match fi.to_str() {                                                
        Some(s) => s,                                                           
        None => return None,                                                    
    };                                                                          
    Some(String::from(st))  
}

返回值是当前用户的主目录名称,包含在 Option<String> 中。

我想尝试使用组合器对此进行重构,以消除行 None => return None,

std::env::home_dir()                                                        
    .and_then(|d| d.file_name())                                            
    .and_then(|f| f.to_str())                                               
    .map(String::from)

但是rustc检测到一个引用超出了它的值。
error: `d` does not live long enough
  --> src/main.rs:33:35
   |
33 |         .and_then(|d| d.file_name())
   |                       -           ^ `d` dropped here while still borrowed
   |                       |
   |                       borrow occurs here
34 |         .and_then(|f| f.to_str())
35 |         .map(String::from)
   |                          - borrowed value needs to live until here

我认为这是因为 Option<&OsStr> 中的引用超出了类型 PathBuf 的值的生命周期。然而,我仍然很难想象在没有值过早超出范围的情况下如何解决这个问题。

为了进一步说明我的目标,这里有一个实现了 Copy trait 的类型的类似示例。

let x = 42u16.checked_add(1234)                                             
    .and_then(|i| i.checked_add(5678))                                      
    .and_then(|i| i.checked_sub(90))                                        
    .map(|i| i.to_string());                                                
println!("{:?}", x); // Some("6864")

在前面的例子中,我肯定忽略了一些与所有权相关的内容。使用 Option<PathBuf> 是可能的吗?

2个回答

3

您说得对,您正在使用从home_dir()返回的PathBuf,但仍然尝试使用引用。

我建议将其存储在变量中,然后从那里开始工作:

fn foo() -> Option<String> {
    let path = std::env::home_dir();
    path.as_ref()
        .and_then(|d| d.file_name())
        .and_then(|f| f.to_str())
        .map(String::from)

}

(Playground)

调用 path.as_ref() 使得一个 Option<&PathBuf> 成为了 and_then 链的起点,而不会消耗原始的拥有权的 PathBuf,至少在 String::from 调用之前需要。


2

扩展Chris的答案:您还可以通过将从第二个and_then开始的链嵌套到传递给第一个and_then的闭包中来解决此问题。这样做的原因是它保持了d(拥有PathBuf)直到对它的借用被释放。

fn foo() -> Option<String> {
    std::env::home_dir().and_then(|d| {
        d.file_name()
           .and_then(|f| f.to_str())
           .map(String::from)
    })
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接