文件原文: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