Wenn Sie die Lebensdauer eines Eingabeparameters an die Lebensdauer des Rückgabewerts binden möchten, müssen Sie einen Lebensdauerparameter für Ihre Funktion definieren und ihn in den Typen des Eingabeparameters und Rückgabewerts referenzieren. Sie können jedem gewünschten Parameter einen beliebigen Namen geben; oft, wenn einige Parameter gibt, nennen wir sie einfach 'a
, 'b
, 'c
usw.
Ihre Db
Art ein Leben lang Parameter nimmt, aber es sollte nicht: a Db
nicht ein bestehendes Objekt referenziert, so dass es hat keine Lebensdauerbeschränkungen.
Für eine korrekte Db
zwingen, die Query
zu überleben, wir 'a
auf den geliehenen Zeiger, anstatt auf die Lebensdauer Parameter auf Db
, die wir entfernt schreiben müssen.
pub fn create_query<'a>(db: &'a Db, query_string: &str) -> Query<'a>
Das ist jedoch nicht genug. Wenn Ihr newtypes verweisen nicht auf ihre 'a
Parameter überhaupt, werden Sie feststellen, dass ein Query
tatsächlich ein überleben kann Db
:
struct Db(*mut());
struct Query<'a>(*mut()); // '
fn create_query<'a>(db: &'a Db, query_string: &str) -> Query<'a> { // '
Query(0 as *mut())
}
fn main() {
let query;
{
let db = Db(0 as *mut());
let q = create_query(&db, "");
query = q; // shouldn't compile!
}
}
Das liegt daran, die standardmäßig Lebensdauer Parameter bivariant, dh der Compiler sind, können Ersetzen Sie den Parameter mit einer längeren oder eine kürzere Lebensdauer, um die Anforderungen des Anrufers zu erfüllen.
Wenn Sie einen ausgeliehenen Zeiger in einer Struktur speichern, wird der Lebensdauerparameter wie kontravariant behandelt: das bedeutet, dass der Compiler den Parameter mit einer kürzeren Lebensdauer, aber nicht mit einer längeren Lebensdauer ersetzen kann.
Wir können den Compiler fragen Sie Ihren Lebenszeit-Parameter als kontra durch Hinzufügen eines ContravariantLifetime
Marker zu unserer Struktur manuell zu behandeln:
use std::marker::ContravariantLifetime;
struct Db(*mut());
struct Query<'a>(*mut(), ContravariantLifetime<'a>);
fn create_query<'a>(db: &'a Db, query_string: &str) -> Query<'a> { // '
Query(0 as *mut(), ContravariantLifetime)
}
fn main() {
let query;
{
let db = Db(0 as *mut());
let q = create_query(&db, ""); // error: `db` does not live long enough
query = q;
}
}
Nun wird der Compiler lehnt richtig die Zuordnung zu query
, die db
überlebt.
Bonus: Wenn wir create_query
ändern ein Verfahren zur Herstellung Db
, eher zu sein, als eine freie Funktion, können wir die Vorteile der Lebensdauer Inferenzregeln des Compilers nehmen und 'a
nicht schreiben überhaupt auf create_query
:
use std::marker::ContravariantLifetime;
struct Db(*mut());
struct Query<'a>(*mut(), ContravariantLifetime<'a>);
impl Db {
//fn create_query<'a>(&'a self, query_string: &str) -> Query<'a>
fn create_query(&self, query_string: &str) -> Query {
Query(0 as *mut(), ContravariantLifetime)
}
}
fn main() {
let query;
{
let db = Db(0 as *mut());
let q = db.create_query(""); // error: `db` does not live long enough
query = q;
}
}
Wenn eine Methode über einen self
-Parameter verfügt, wird der Compiler es vorziehen, die Lebensdauer dieses Parameters mit dem Ergebnis zu verknüpfen, auch wenn andere Parameter mit Lebensdauern vorhanden sind. Für freie Funktionen ist eine Inferenz jedoch nur möglich, wenn nur ein Parameter eine Lebensdauer hat. Wegen des Parameters query_string
, der vom Typ &'a str
ist, gibt es 2 Parameter mit einer Lebensdauer, sodass der Compiler nicht ableiten kann, mit welchem Parameter das Ergebnis verknüpft werden soll.
Danke! Mit diesem Entwurf wird die Testfunktion, die ich mit schlechter Lebensdauer geschrieben habe, jetzt vom Compiler korrekt zurückgewiesen. –