rust库-ouroboros中文文档

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

posted @ 2024-08-21 23:18  余为民同志  阅读(9)  评论(0编辑  收藏  举报