rust庫-ouroboros中文文件

余为民同志發表於2024-08-21

文件原文:https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html

屬性宏ouroboros::self_referencing

#[self_referencing]

此宏用於將常規結構轉換為自引用結構。舉個例子:

use ouroboros::self_referencing;

#[self_referencing]
struct MyStruct {
    int_data: i32,
    float_data: f32,
    #[borrows(int_data)]
    // `this這個生命週期是由#[self_referending]宏建立的
    // 並且應該用在所有由#[borrows]宏標記的引用上
    int_reference: &'this i32,
    #[borrows(mut float_data)]
    float_reference: &'this mut f32,
}

fn main() {
    // 構建器由#[self_referending]宏建立
    // 並用於建立結構體
    let mut my_value = MyStructBuilder {
        int_data: 42,
        float_data: 3.14,

        // 注意構造器中的欄位名稱
        // 是struct +'_builder'結構中的欄位名
        // 即:{field_name}_builder
        // 為欄位賦值的閉包將被傳遞
        // 對#[borrows]宏中定義的欄位的引用

        int_reference_builder: |int_data: &i32| int_data,
        float_reference_builder: |float_data: &mut f32| float_data,
    }.build();

    // 不能直接訪問原始結構體中的欄位
    // 構造器建立的訪問器呼叫的方法為 borrow_{field_name}()

    // 列印 42
    println!("{:?}", my_value.borrow_int_data());
    // 列印 3.14
    println!("{:?}", my_value.borrow_float_reference());
    // 設定float_data值為84.0
    my_value.with_mut(|fields| {
        **fields.float_reference = (**fields.int_reference as f32) * 2.0;
    });

    // 我們可以持有這個引用...
    let int_ref = *my_value.borrow_int_reference();
    println!("{:?}", *int_ref);
    // 只要結構體還活著
    drop(my_value);
    // 下面的使用是會報錯的
    // println!("{:?}", *int_ref);
}

透過RustRover觀察建立出來的結構體包裝出了以下方法:

返回值型別為:

為了解釋這個crate的特點和侷限性,一些定義是必要的:

定義

  • 不可變借用欄位:至少被一個其他欄位不可變借用的欄位。
  • 可變借用欄位:正好由另一個欄位可變借用的欄位。
  • self-referencing field:至少借用一個其他欄位的欄位。
  • head field:不借用任何其他欄位的欄位,即不自引用的欄位。 這不包括具有空借用(#[borrows()])的欄位
  • tail field:未被任何其他欄位借用的欄位。

使用

要建立自引用結構體,必須編寫結構體定義並將#[self_referending]置於頂部。對於每個借用其他欄位的欄位,必須將#[borrows()]放在頂部,並在括號內放置它借用的欄位列表。可以加上Mut字首,以指示需要可變借用。例如,#[borrows(a, b, mut c)]表示前兩個欄位需要不可變地借入,第三個欄位需要可變地借入。你也可以使用不帶任何引數的#[borrows()]來表示一個欄位,該欄位最終將從結構中借入,但在第一次建立時不會借入任何東西。例如,你可以在一個欄位上使用:error: Option<&'this str>

您必須遵守這些限制

  • 欄位必須在第一次借用之前宣告。
  • 正常的借用規則適用,例如,一個欄位不能被借用兩次。
  • 使用'this生存期的欄位必須有一個對應的#[borrows()]註釋。

違反它們將導致錯誤訊息直接指出違反的規則。

該crate的靈活性

上面的示例使用純引用作為結構的自引用部分,但您可以使用依賴於結構中物件生存期的任何內容。例如,您可以 做這樣的事情:

use ouroboros::self_referencing;

pub struct ComplexData<'a, 'b> {
    aref: &'a i32,
    bref: &'b mut i32,
    number: i32,
}

impl<'a, 'b> ComplexData<'a, 'b> {
    fn new(aref: &'a i32, bref: &'b mut i32, number: i32) -> Self {
        Self { aref, bref, number }
    }

    /// Copies the value aref points to into what bref points to.
    fn transfer(&mut self) {
        *self.bref = *self.aref;
    }

    /// Prints the value bref points to.
    fn print_bref(&self) {
        println!("{}", *self.bref);
    }
}

fn main() {
    #[self_referencing]
    struct DataStorage {
        immutable: i32,
        mutable: i32,
        #[borrows(immutable, mut mutable)]
        #[not_covariant]
        complex_data: ComplexData<'this, 'this>,
    }

    let mut data_storage = DataStorageBuilder {
        immutable: 10,
        mutable: 20,
        complex_data_builder: |i: &i32, m: &mut i32| ComplexData::new(i, m, 12345),
    }.build();
    data_storage.with_complex_data_mut(|data| {
        // Copies the value in immutable into mutable.
        data.transfer();
        // Prints 10
        data.print_bref();
    });
}

協變性

在Rust語言中,許多型別具有一種稱為“協變性”的屬性。實際上,這意味著像Box<&'this i32>這樣的協變型別可以用作Box<&'a i32>,只要’a比’this小。由於生命週期更短,因此它不會違反原始型別指定的生命週期。這與Fn(&'this i32)不同,它不是協變的。你不能給這個函式一個生命週期短於’this的引用,因為函式需要至少與’this一樣長的東西。不幸的是,從宏內部無法輕易確定一個型別是否具有協變性。因此,您可能會收到一個編譯器錯誤,讓您知道宏不確定一個特定欄位是否使用了協變型別。新增#[covariant]或#[not_covariant]可以解決此問題。

這些註解控制是否為該欄位生成borrow_*方法。錯誤地使用這些標籤將導致編譯錯誤。它們不可能被不安全地使用。

生成的專案列表

MyStruct::new(fields...) -> MyStruct

基本建構函式。它按照您在中宣告的順序接受每個欄位的值。對於head欄位,你只需要傳入它應該有的值,它將被移動到輸出中。對於自引用欄位,您必須提供一個函式或閉包,它根據它借用的值來建立值。使用前面的示例#[借入(a,b,mut c)]的欄位將需要一個型別為FnOnce(a: &, b: &, c: &mut _) -> _的函式。具有空借用註釋(#[借入()])的欄位應將其值直接傳遞給。使用前面的Option<&'this str>示例的欄位將要求輸入None。不要傳遞函式。不收200元。

MyStruct::new_async(fields...) -> MyStruct

MyStruct::new_async_send(fields...) -> MyStruct

MyStructBuilder

MyStructAsyncBuilder

MyStructAsyncSendBuilder

MyStruct::try_new<E>(fields...) -> Result<MyStruct, E>

MyStruct::try_new_async<E>(fields...) -> Result<MyStruct, E>

MyStruct::try_new_async_send<E>(fields...) -> Result<MyStruct, E>

MyStruct::try_new_or_recover_async<E>(fields...) -> Result<MyStruct, (E, Heads)>

MyStruct::try_new_or_recover_async_send<E>(fields...) -> Result<MyStruct, (E, Heads)>

MyStruct::with_FIELD<R>(&self, user: FnOnce(field: &FieldType) -> R) -> R

MyStruct::borrow_FIELD(&self) -> &FieldType

MyStruct::with_FIELD_mut<R>(&mut self, user: FnOnce(field: &mut FieldType) -> R) -> R

MyStruct::with<R>(&self, user: FnOnce(fields: AllFields) -> R) -> R

MyStruct::with_mut<R>(&self, user: FnOnce(fields: AllFields) -> R) -> R

MyStruct::into_heads(self) -> Heads

相關文章