2017-05-26 2 views
0

Ich habe eine Struktur, die einige Sachen enthält. Ich implementiere das Merkmal Iterator für diese Struktur und gebe ein Tupel von Referenzen auf interne Daten in der Struktur zurück. Das erfordert, dass ich zumindest einige Dinge mit Lebenszeit annotieren. Was ich möchte, ist die Lebensdauer Annotation zu minimieren, vor allem, wenn es um andere Strukturen geht, die die ursprüngliche Struktur als Mitglied haben.Kann ich die lebenslange Verschmutzung von einer Struktur begrenzen?

einige Code:

pub struct LogReader<'a> { 
    data:String, 
    next_fn:fn(&mut LogReader)->Option<(&'a str,&'a [ConvertedValue])>, 
//... 
} 

pub struct LogstreamProcessor { 
    reader: LogReader, // doesn't work without polluting LogstreamProcessor with lifetimes 
//... 
} 

impl<'a> Iterator for LogReader<'a > { 
    type Item = (&'a str,&'a[ConvertedValue]); 

    fn next(&mut self) -> Option<(&'a str,&'a[ConvertedValue])>{(self.next_fn)(self)} 

} 

impl <'a> LogReader<'a> { 
    pub fn new(textFile:Option<bool>) -> LogReader<'a> { 
     LogReader { 
      next_fn:if textFile.unwrap_or(false) { LogReader::readNextText }else{ LogReader::readNextRaw }, 
      data: "blah".to_string() 
     } 
    } 

    fn readNextText(&mut self)->Option<(&str,&[ConvertedValue])>{unimplemented!();} 
    fn readNextRaw(&mut self)->Option<(&str,&[ConvertedValue])>{unimplemented!();} 
} 
+0

"und ein Tupel von Verweisen auf interne Daten in der Struktur zurückgeben" Sind Sie sicher, dass Sie keine Methode, sondern ein Funktionsobjekt in einem Feld wollen? Im Moment benutzen Sie 'next_fn' als wäre es eine Methode. –

+0

Das Feld enthält einfach einen Zeiger auf eine Methode auf dem Objekt. Es ist nicht nett, aber ich portiere Python, also gibt es eine Menge, die nicht gut übersetzt. –

+0

Methoden aus Python übersetzen schön ... in [Methoden] (https://doc.rust-lang.org/book/method-syntax.html). –

Antwort

1

Kann ich begrenzen die Lebensdauer der Verschmutzung durch eine Struktur?

generisch, wenn man sie in einem Ihrer Struktur der Felder, dann Sie können nicht verwenden. Sie werden aus sehr guten Gründen explizit gemacht (siehe Why are explicit lifetimes needed in Rust?), und sobald Sie eine Struktur mit Objekten haben, die explizite Lebensdauern benötigen, müssen sie propagiert werden.

Beachten Sie, dass dies in der Regel kein Problem für die Verbraucher der Struktur ist, da die konkreten Lebensdauern werden dann vom Compiler auferlegt:

struct NameRef<'a>(&'a str); 

let name = NameRef("Jake"); // 'a is 'static 

Man könnte auch das „Rauschen“ etwas mildern über die Umsetzung der next mit der Definition von Self::Item.

impl<'a> Iterator for LogReader<'a > { 
    type Item = (&'a str,&'a[ConvertedValue]); 

    fn next(&mut self) -> Option<Self::Item> { 
     (self.next_fn)(self) 
    } 
} 

jedoch Ihr Anliegen verbirgt tatsächlich ein ernstes Problem: Im Gegensatz zu Ihnen erwähnt haben, sind die Werte von next zurück sind nicht notwendigerweise interne Daten aus der Struktur. Sie leben tatsächlich so lange wie die generische Lebenszeit 'a, und nichts innerhalb LogReader ist tatsächlich durch diese Lebenszeit gebunden.

Das bedeutet zweierlei:

(1) I eine Funktion, die etwas ganz anderes, gibt passieren könnte und es würde gut funktionieren:

static NO_DATA: &[()] = &[()]; 
fn my_next_fn<'a>(reader: &mut LogReader<'a>) -> Option<(&'a str, &'a[ConvertedValue])> { 
    Some(("wat", NO_DATA)) 
} 

(2) Auch wenn ich meine Funktion wollte um etwas aus den internen Daten des Protokolllesers zurückzugeben, würde es nicht funktionieren, weil die Lebensdauern überhaupt nicht übereinstimmen. Lassen Sie uns versuchen es trotzdem, um zu sehen, was passiert:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements 
    --> src/main.rs:26:12 
    | 
26 |  Some((&reader.data[0..4], DATA)) 
    |   ^^^^^^^^^^^^^^^^^ 
    | 
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 25:88... 
    --> src/main.rs:25:89 
    | 
25 | fn my_next_fn<'a>(reader: &mut LogReader<'a>) -> Option<(&'a str, &'a[ConvertedValue])> { 
    | _________________________________________________________________________________________^ starting here... 
26 | |  Some((&reader.data[0..4], DATA)) 
27 | | } 
    | |_^ ...ending here 
note: ...so that reference does not outlive borrowed content 
    --> src/main.rs:26:12 
    | 
26 |  Some((&reader.data[0..4], DATA)) 
    |   ^^^^^^^^^^^ 
note: but, the lifetime must be valid for the lifetime 'a as defined on the body at 25:88... 
    --> src/main.rs:25:89 
    | 
25 | fn my_next_fn<'a>(reader: &mut LogReader<'a>) -> Option<(&'a str, &'a[ConvertedValue])> { 
    | _________________________________________________________________________________________^ starting here... 
26 | |  Some((&reader.data[0..4], DATA)) 
27 | | } 
    | |_^ ...ending here 
note: ...so that expression is assignable (expected std::option::Option<(&'a str, &'a [()])>, found std::option::Option<(&str, &[()])>) 
    --> src/main.rs:26:5 
    | 
26 |  Some((&reader.data[0..4], DATA)) 
    |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

... wo der anonyme Lebensdauer # 1 die Lebensdauer des Log-Leser ist:

static DATA: &[()] = &[()]; 

fn my_next_fn<'a>(reader: &mut LogReader<'a>) -> Option<(&'a str, &'a[ConvertedValue])> { 
    Some((&reader.data[0..4], DATA)) 
} 

fn main() { 
    let mut a = LogReader { 
     data: "This is DATA!".to_owned(), 
     next_fn: my_next_fn 
    }; 

    println!("{:?}", a.next()); 
} 

Der Compiler würde dies werfen Sie. Das Erzwingen von &mut LogReader mit einer Lebensdauer 'a (&'a mut LogReader<'a>) würde zu weiteren Lebensdauerproblemen führen, wenn versucht wird, Iterator zu implementieren. Dies beschränkt sich im Wesentlichen darauf, dass 'a mit Verweisen auf Werte von LogReader selbst nicht kompatibel ist.

Also, wie sollen wir das beheben?

aber das bedeutet nicht die Tatsache ändern, dass der Rückgabetyp references und so Lebenszeit Anmerkungen kommen in sie

Obwohl das nicht korrekt ist (seit Lebensdauer elision in einigen Fällen auftreten können), dass gibt einen Hinweis auf die Lösung: entweder vermeiden, Referenzen überhaupt zu liefern oder Daten an ein separates Objekt zu delegieren, so dass 'a an die Lebensdauer dieses Objekts gebunden werden kann. Der letzte Teil der Antwort auf Ihre Frage ist in Iterator returning items by reference, lifetime issue.

+0

'Beachten Sie, dass dies normalerweise kein Problem für die Verbraucher der Struktur ist, da die konkreten Lebensdauern dann vom Compiler auferlegt werden. Das ist meine Frage, wie kann ich das für den LogstreamProcessor arbeiten lassen. –

+0

@CamdenNarzt Wie ich zu Beginn der Antwort gesagt habe, benötigt man auch für diese Struktur eine explizite Lebensdauer. Der Compiler löst das nicht automatisch auf. –

Verwandte Themen