在存在借用的情况下,价值在闭包和组合器中过早下降

Value getting dropped too early inside closure and combinator while borrow exists

我遇到了一个问题,即在闭包中 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))  
}

return 值是 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 特征。

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> 这可能吗?

您是对的,您正在使用从 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.

扩展 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)
    })
}