2015-01-11 9 views
6

私はFFIを使用して、所有権の強い概念(重要であればlibnotmuch API)を持つC APIに対していくつかの錆コードを書いています。構造体の寿命を '親'構造体の寿命に制限するにはどうしたらいいですか?

APIの主なエントリポイントは、データベースです。私は、データベースからクエリオブジェクトを作成することができます。データベースとクエリ(および他の多くのオブジェクト)のデストラクタ関数を提供します。

ただし、クエリを作成したデータベースよりもクエリの保存期間が長くなることはありません。データベースデストラクタ関数は、破棄されなかったクエリなどを破棄します。その後、クエリデストラクタは機能しません。

これまでのところ、データベースとクエリを作成して操作することができます。しかし、私は生涯の境界をエンコードするのが難しいです。

私はこのような何かをしようとしている:

struct Db<'a>(...) // newtype wrapping an opaque DB pointer 
struct Query<'a>(...) // newtype wrapping an opaque query pointer 

私は基礎となるCのデストラクタ関数を呼び出して、これらのそれぞれについて、Dropの実装を持っています。私は、クエリは、DBより長生きすることはできません返されるように?秒の場所に置くために何を知らない

pub fun create_query<?>(db: &Db<?>, query_string: &str) -> Query<?> 

そして、クエリを作成する機能を持っています。

このAPIの生涯の制約をどのようにモデル化できますか?

答えて

5

入力パラメータの有効期間を戻り値の有効期間にバインドするには、関数の有効期間パラメータを定義し、入力パラメータと戻り値の型で参照する必要があります。この生涯パラメータに任意の名前を付けることができます。多くの場合、いくつかのパラメータがあるとき、私達はちょうどそれらに名前を付ける'a'b'cなど

あなたDbタイプは寿命パラメータを取りますが、それはいけません:Dbは、既存のオブジェクトを参照するので、それはありません寿命の制約はありません。

が正しくQueryより長生きするDbを強制するために、我々はむしろ我々がちょうど取り除かDbの寿命パラメータに比べて、借りたポインタに'aを記述する必要があります。

pub fn create_query<'a>(db: &'a Db, query_string: &str) -> Query<'a> 

しかし、これでは不十分です。あなたのnewtypesが全く彼らの'aパラメータを参照しない場合は、Queryが実際より長生きできることがわかります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! 
    } 
} 

かもしれません呼び出し元の要件を満たすために、パラメータをより短いまたはに置き換えてください。コンパイラはなく、長い寿命を持つ、短い生涯でパラメータを置換してもよい意味:あなたは構造体で借りポインタを格納する場合

は、寿命パラメータは、反変として扱われます。

私たちは、構造体へContravariantLifetimeマーカーを追加することにより、手動で反変としてあなたの寿命パラメータを治療するためのコンパイラを依頼することができます。今すぐ

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; 
    } 
} 

、コンパイラが正しくdbをoutlivesれ、queryへの割り当てを拒否します。


ボーナス:我々はDbの方法ではなく、自由に機能するcreate_queryを変更した場合、我々はコンパイラの生涯の推論規則を利用してcreate_query上ですべての'aを書き込むことはできません。

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; 
    } 
} 

メソッドにselfパラメータがある場合、コンパイラは、存続期間を持つ他のパラメータがある場合でも、そのパラメータの有効期間を結果とリンクすることをお勧めします。しかし、自由な関数の場合、推論は、1つのパラメータに存続期間がある場合にのみ可能です。ここでは、タイプ&'a strquery_stringパラメータのため、2つのパラメータが存続するため、コンパイラは結果をリンクするパラメータを推測できません。

+0

ありがとう!この設計では、私は悪い生涯で書いたテスト関数は、コンパイラによって正しく拒否されるようになりました。 –

関連する問題