Jeśli chcesz powiązać czas życia parametru wejściowego z czasem życia zwracanej wartości, musisz zdefiniować parametr cyklu życia dla swojej funkcji i odnieść go do typów parametrów wejściowych i wartości zwracanej. Możesz nadać dowolną nazwę temu parametrowi życia; Często, gdy istnieje kilka parametrów, po prostu wymienić je 'a
, 'b
, 'c
itp
Twój typ Db
przyjmuje parametr życiu, ale to nie powinno: a Db
nie odnosi się do istniejącego obiektu, a więc nie ma ograniczeń na całe życie.
Aby poprawnie zmusić Db
do przeżyje Query
, musimy napisać 'a
na pożyczonym wskaźnik, zamiast parametru dożywotnią na Db
że po prostu usunięte.
pub fn create_query<'a>(db: &'a Db, query_string: &str) -> Query<'a>
To jednak za mało. Jeśli twoje newtypes nie odwoływać ich 'a
parametr w ogóle, przekonasz się, że Query
może faktycznie przeżyć 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!
}
}
To dlatego, że domyślnie parametry lifetime są bivariant, czyli kompilator może Parametr należy zastąpić dłuższym czasem trwania o mniejszą trwałość, aby spełnić wymagania osoby dzwoniącej.
Podczas przechowywania pożyczoną wskaźnik do struktury, parametr życia jest traktowana jako kontrawariantny: oznacza to, że kompilator może zastąpić parametr z krótszą żywotność, ale nie z dłuższą żywotnością.
Możemy poprosić kompilator traktować jako parametr dożywotnią kontrawariantny ręcznie dodając ContravariantLifetime
marker do naszej struktury:
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;
}
}
Teraz kompilator poprawnie odrzuca zadanie do query
, które żyją dłużej db
.
Bonus: Jeśli zmienimy create_query
być metoda Db
zamiast wolnej funkcji, możemy skorzystać z zasadami życia wnioskowania kompilator i nie pisać 'a
w ogóle na 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;
}
}
Gdy metoda ma parametr self
, kompilator będzie preferował łączenie czasu życia tego parametru z wynikiem, nawet jeśli istnieją inne parametry z czasem życia. Jednak w przypadku darmowych funkcji wnioskowanie jest możliwe tylko wtedy, gdy tylko jeden parametr ma całe życie. Tutaj, z powodu parametru query_string
, który jest typu &'a str
, istnieją 2 parametry z czasem życia, więc kompilator nie może określić, z którym parametrem chcemy powiązać wynik.
Dzięki! Przy takim projekcie funkcja testu, którą napisałem ze złym czasem życia, jest teraz poprawnie odrzucana przez kompilator. –