Khi bạn muốn gắn kết tuổi thọ của tham số đầu vào với giá trị trả về, bạn cần phải xác định tham số trọn đời trên hàm của bạn và tham chiếu nó trong các kiểu tham số đầu vào và giá trị trả về. Bạn có thể cho bất kỳ tên nào bạn muốn tham số trọn đời này; Thông thường, khi có rất ít thông số, chúng ta chỉ cần đặt tên cho chúng 'a
, 'b
, 'c
vv
loại Db
bạn mất một tham số đời, nhưng nó không nên: a Db
không tham chiếu tới đối tượng hiện có, vì vậy nó không có ràng buộc suốt đời.
Buộc đúng Db
để sống lâu hơn các Query
, chúng ta phải viết 'a
trên con trỏ vay, chứ không phải trên các tham số đời trên Db
mà chúng ta vừa gỡ bỏ.
pub fn create_query<'a>(db: &'a Db, query_string: &str) -> Query<'a>
Tuy nhiên, điều đó không đủ. Nếu newtypes bạn không tham khảo thông số 'a
của họ ở tất cả, bạn sẽ thấy rằng một Query
thực sự có thể sống lâu hơn một 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!
}
}
Đó là bởi vì, theo mặc định, các thông số đời là bivariant, tức là trình biên dịch có thể thay thế thông số có thời lượng ngắn hơn là hoặc để đáp ứng các yêu cầu của người gọi.
Khi bạn lưu trữ một con trỏ được mượn trong cấu trúc, tham số suốt đời được coi là contravariant: điều đó có nghĩa là trình biên dịch có thể thay thế tham số với thời lượng ngắn hơn, nhưng không có thời lượng lâu hơn.
Chúng tôi có thể yêu cầu trình biên dịch để điều trị thông số tuổi thọ của bạn như contravariant bằng tay bằng cách thêm một dấu ContravariantLifetime
để struct của chúng tôi:
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;
}
}
Bây giờ, trình biên dịch từ chối một cách chính xác sự phân công để query
, mà outlives db
.
Bonus: Nếu chúng ta thay đổi create_query
trở thành một phương pháp Db
, chứ không phải là một chức năng miễn phí, chúng ta có thể tận dụng các quy tắc cuộc đời suy luận của trình biên dịch và không viết 'a
ở tất cả trên 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;
}
}
Khi phương thức có tham số self
, trình biên dịch sẽ thích liên kết thời gian tồn tại của tham số đó với kết quả, ngay cả khi có các tham số khác có thời gian tồn tại. Tuy nhiên, đối với các hàm miễn phí, suy luận chỉ có thể xảy ra nếu chỉ có một tham số có tuổi thọ. Ở đây, vì tham số query_string
, là loại &'a str
, có 2 tham số với toàn bộ thời gian, do đó trình biên dịch không thể suy ra tham số nào chúng tôi muốn liên kết kết quả với.
Cảm ơn! Với thiết kế này, chức năng thử nghiệm mà tôi đã viết với thời gian tồn tại xấu hiện đã bị trình biên dịch từ chối một cách chính xác. –