Estoy enfrentando un problema con la caída de un valor mientras todavía está prestado dentro de un Option, en un cierre, pero me cuesta entender exactamente qué está sucediendo. Para ilustrar, aquí hay un ejemplo práctico de lo que realmente estoy tratando de lograr:

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

El valor de retorno es el nombre base del directorio de inicio del usuario actual dentro de Option<String>.

Pensé que intentaría refactorizar esto con combinadores para eliminar las líneas None => return None,.

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

Pero rustc detecta una referencia que sobrevive a su valor.

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

creo esto se debe a que la referencia en Option<&OsStr> está superando el valor del tipo PathBuf. Sin embargo, todavía estoy teniendo dificultades para descubrir cómo abordar esto sin que el valor se salga del alcance demasiado pronto.

Para ilustrar mejor lo que estoy tratando de lograr, aquí hay un ejemplo similar con un tipo que implementa el rasgo Copiar.

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")

Así que definitivamente estoy pasando por alto algunas cosas relacionadas con la propiedad en el ejemplo anterior. ¿Es esto posible con Option<PathBuf>?

4
James 24 feb. 2017 a las 01:41

2 respuestas

La mejor respuesta

Tienes razón en que estás consumiendo el PathBuf devuelto por home_dir() pero aún estás tratando de usar referencias.

Lo mantendría en una variable y trabajaría desde allí:

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)

}

(Zona de juegos)

La llamada a path.as_ref() hace un Option<&PathBuf> como punto de partida para la cadena de and_then, sin consumir el PathBuf original que se necesita al menos hasta String::from .

3
Chris Emerson 24 feb. 2017 a las 00:17

Ampliando la respuesta de Chris: También puede solucionar el problema anidando la cadena comenzando desde el segundo and_then en el cierre pasado al primero and_then. Esto funciona porque mantiene d (que posee un PathBuf) vivo hasta que se liberan los préstamos.

fn foo() -> Option<String> {
    std::env::home_dir().and_then(|d| {
        d.file_name()
           .and_then(|f| f.to_str())
           .map(String::from)
    })
}
2
Francis Gagné 24 feb. 2017 a las 05:54