2016-05-01 6 views
0

Ich glaube, ich ein gutes Verständnis von Leben haben, aber was ich habe gelesen, mit nicht kreuzen, was der Compiler sagt, wenn es um Verschlüsse kommt;)Lebensdauer der Variable in der Karte/flat_map in Rust

Ich habe eine Funktion mit der Unterschrift:

fn split_to_words(content: &str) -> Vec<String> 

mit for Schleifen sieht es wie folgt aus:

let mut acc: Vec<String> = Vec::new(); 
for line in content.lines() { 
    if line.is_empty() { continue }; 
    for word in line.split_whitespace() { 
     acc.push(word.to_lowercase()); 
    } 
} 

und mit Iteratoren:

aber ich am Ende mit einem Fehler auf:

error: `x` does not live long enough 

.flat_map(|x: String| x.split_whitespace()) 
        ^

Antwort

3

Ihr Iterator-Stil-Code ist nicht genau das gleiche wie Ihr Imperativ-Stil-Code: im Imperativ Code, was Sie tun split_whitespace vor to_lowercase, während in dem Iterator-Code, Sie das Gegenteil tun. Es stellt sich heraus, dass split_whitespace vorher effizienter ist, da split_whitespace nicht Strings zuweisen muss; es gibt nur Slices in das angegebene String-Slice zurück. Auf der anderen Seite muss to_lowercase eine neue Zeichenfolge zuweisen, so dass wir eine Zuordnung speichern können.

let acc: Vec<String> = content.lines() 
     .filter(|x| !x.is_empty()) 
     .flat_map(|x| x.split_whitespace()) 
     .map(|x| x.to_lowercase()) 
     .collect(); 
2

Es ist hilfreich, die gesamte Fehlermeldung zu überprüfen:

error: `x` does not live long enough 
note: reference must be valid for the method call at ... 
note: ...but borrowed value is only valid for the scope 
     of parameters for function at ... 

Wenn flat_map genannt wird, Sie Pass Eigentum der String zur Schließung. Der Abschluss versucht jedoch, einen Iterator zurückzugeben, der Verweise auf dem String enthält. Da jede String nach Beendigung des Closure-Aufrufs freigegeben wird, wäre der Verweis darauf ungültig, sodass der Compiler Sie daran hindert, dies zu tun.

ich wahrscheinlich die for -loop Variante oder eine ähnliche Version verwenden würde:

fn split_to_words(content: &str) -> Vec<String> { 
    let lines = content.lines() 
      .filter(|x| !x.is_empty()) 
      .map(|x| x.to_lowercase()); 

    let mut result = Vec::new(); 

    for line in lines { 
     for word in line.split_whitespace() { 
      result.push(word.to_string()); 
     } 
    } 

    result 
} 

fn main() ->() { 
    println!("{:?}", split_to_words("hello world")); 
} 

In diese besonderen Fall aber, ich glaube, Sie es als schreiben konnte:

fn split_to_words(content: &str) -> Vec<String> { 
    content.split_whitespace().map(str::to_lowercase).collect() 
} 

Newlines sind Whitespace, also müssen Sie lines nicht verwenden. Dies beseitigt auch die Möglichkeit von Leerwerten.

+0

Ich wusste nicht, über 'str :: to_lowercase', sieht es ähnlich, wie es in Java getan. Danke dafür! Ich dachte, dass es schön wäre, wenn es so funktioniert! – almendar

Verwandte Themen