2017-12-30 34 views
1

mit Ich versuche, den folgenden vereinfachten Code zu kompilieren:Wert „leben nicht lange genug“, aber nur dann, wenn ein Funktionszeiger

type FunctionType<'input> = fn(input_string: &'input str) -> Result<(), &'input str>; 

fn okay<'input>(_input_string: &'input str) -> Result<(), &'input str> { 
    Ok(()) 
} 

fn do_stuff_with_function(function: FunctionType) { 
    let string = String::new(); 
    match function(string.as_str()) { 
     Ok(_) => {}, 
     Err(_) => {}, 
    } 

} 

fn main() { 
    do_stuff_with_function(okay);  
} 

Der Spielplatz klagt:

error[E0597]: `string` does not live long enough 
    --> src/main.rs:13:20 
    | 
13 |  match function(string.as_str()) { 
    |     ^^^^^^ does not live long enough 
... 
18 | } 
    | - borrowed value only lives until here 
    | 
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 11:1... 
    --> src/main.rs:11:1 
    | 
11 |/fn do_stuff_with_function(function: FunctionType) { 
12 | |  let string = String::new(); 
13 | |  match function(string.as_str()) { 
14 | |   Ok(_) => {}, 
... | 
17 | |  
18 | | } 
    | |_^ 

Ich verstehe, warum der Fehler unter anderen Umständen auslösen würde: string lebt nur so lange wie die Ausführung von do_stuff_with_function, aber do_stuff_with_function gibt den Wert von function Aufruf zurück, der eine Referenz für die gleiche Lebenszeit auf seinem Eingabewert enthält h., string.

Allerdings bin ich auf drei Punkte verwirrt:

  1. ich das Ergebnis des Funktionsaufrufs match, dann () Return für beide Zweige. Warum ist die Lebensdauer des Werts, der von function zurückgegeben wird, von Bedeutung, wenn er bedingungslos verworfen wird?
  2. Wenn ich den Aufruf des Parameters function mit einem direkten Verweis auf okay (die eine identische Signatur hat) ersetzen, kompiliert es ohne Beanstandung.
  3. Die Fehlermeldung schlägt vor (obwohl nicht direkt angeben?), Dass die erforderliche Lebensdauer bereits die tatsächliche Lebensdauer ist.

Antwort

2

TL; DR: Verwenden the for<'a> syntax eine bestimmte Lebensdauer für die Funktion haben, anstatt eine aus dem Zusammenhang zu bringen, wo es verwendet wird.


Lebensdauer elision auf args von do_stuff_with_function versteckt, was hier los ist. Ihre tatsächliche Lebensdauer ist:

fn do_stuff_with_function<'a>(function: FunctionType<'a>) { 

Diese Lebensdauer in Funktionsargument erklärt bedeutet nicht „einige zufällige kurze Lebensdauer ich später kommen werde“, sondern „ein Leben lang, die bereits an dem Punkt existiert diese zu nennen Funktion".

Diese lebenslangen Anmerkungen sind keine Platzhalter, sondern eher wie Bezeichner, um zu verfolgen, woher die Werte kommen und wohin sie gehen. Sie können verwendet werden, beispielsweise eine Situation wie diese zu klären:

fn do_stuff_with_function<'a>(function: FunctionType<'a>, t: &'a str) { 
    match function(t) { 
     Ok(_) => {}, 
     Err(_) => {}, 
    } 
} 

fn main() { 
    let string = String::new(); 
    do_stuff_with_function(okay, string.as_str());  
} 

aber das Problem ist lösbar in Rust, sondern braucht nur eine erweiterte Syntax. Zum Zwecke der Erklärung, zunächst wollen wir ändern es.

fn do_stuff_with_function<'a, F>(function: F) where F: Fn(&'a str) -> Result<(), &'a str> { 

Das bedeutet, „eine Kopie von do_stuff_with_function für jede eindeutige F machen, die alles sein kann, die sich wie eine Funktion &'a str (etc.) nehmen sieht Dies ist im Wesentlichen das Gleiche (+ erlaubt Closures) wie Ihr Code.Allerdings musste ich noch die Lebenszeit als gebunden an den Aufruf von do_stuff_with_function<'a> bezeichnen.

Also hier ist ein freaky type magic:

fn do_stuff_with_function<F>(function: F) where F: for<'a> Fn(&'a str) -> Result<(), &'a str> { 

die sich von do_stuff_with_function zu Definition des Fn mit for<'a> Syntax bewegen Definition der Lebensdauer ermöglicht. Auf diese Weise ist es spezifisch für die F, anstatt do_stuff_with_function Argumente.

+0

Dies funktioniert für meinen Fall! Ich bin mir immer noch nicht sicher, warum Punkt (1) in meiner ursprünglichen Frage nicht ausreicht, um den Compiler zufrieden zu stellen, und traurig ist, dass Sie den Alias ​​inline (der echte, den ich habe, ist ziemlich riesig), aber froh, dass dies möglich ist mit freaky Art Magie. – stuffy

+2

@stuffy, Sie müssen den Alias ​​nicht inline einfügen. 'für <'_>' kann hinzugefügt werden. [Playground] (https://play.rust-lang.org/?gist=bbd442124db421e7201baa5b29ea7829&version=stable) – red75prime

+2

Sie können auch '<'input>' vom Typ Alias ​​entfernen. 'type FunctionType = fn (input_string: & str) -> Ergebnis <(), &str>;' Lifetime elision wird das Richtige tun. – red75prime

Verwandte Themen