2017-02-23 3 views
3

Je suis confronté à un problème avec une valeur abandonnée alors qu'elle est encore empruntée dans un Option, dans une fermeture, mais j'ai du mal à saisir exactement ce qui est passe. Pour illustrer, voici un exemple concret de ce que je suis en train d'essayer d'atteindre:La valeur se lâche trop tôt à l'intérieur de la fermeture et le combinateur alors que l'emprunt existe

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

La valeur de retour est le nom de base du répertoire de l'utilisateur courant à l'intérieur Option<String>.

Je pensais que j'essaierais de refactoriser cela avec des combinateurs pour se débarrasser des lignes None => return None,.

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

Mais rustc détecte une référence qui survit à sa valeur.

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 

Je pense c'est parce que la référence en Option<&OsStr> est la valeur de vivre plus longtemps que le type PathBuf. Cependant, j'ai encore du mal à comprendre comment aborder cette question sans que la valeur ne soit hors de portée trop tôt.

Pour illustrer davantage ce que j'essaie d'obtenir, voici un exemple similaire avec un type qui implémente le trait Copier.

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

Donc, je néglige définitivement quelques choses liées à la propriété dans l'exemple précédent. Est-ce possible avec Option<PathBuf>?

Répondre

2

Vous avez raison de dire que vous consommez le PathBuf renvoyé par home_dir() mais en essayant toujours d'utiliser des références.

je garderais dans une variable, et le travail à partir de là:

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)

L'appel à path.as_ref() fait un Option<&PathBuf> comme point de départ de la chaîne de and_then, sans consommer l'original appartenait PathBuf qui est nécessaire au moins jusqu'à String::from.

2

Extension de la réponse de Chris: Vous pouvez également résoudre le problème en imbriquant la chaîne à partir de la seconde and_then dans la fermeture transmise au premier and_then. Cela fonctionne car il conserve d (qui possède un PathBuf) en vie jusqu'à ce que les emprunts sont libérés.

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