#self-referential #self #reference #lifetime #intrusive #borrowing #data-structures

self_cell

稳定Rust中安全使用的无proc-macro自我引用结构体

4个版本 (稳定)

1.0.4 2024年5月3日
1.0.3 2023年12月20日
1.0.2 2023年11月10日
1.0.1 2023年6月13日
0.9.0 2021年6月19日

#34 in Rust模式

Download history 131993/week @ 2024-05-02 123320/week @ 2024-05-09 121637/week @ 2024-05-16 125398/week @ 2024-05-23 130389/week @ 2024-05-30 124066/week @ 2024-06-06 130766/week @ 2024-06-13 127584/week @ 2024-06-20 128817/week @ 2024-06-27 142022/week @ 2024-07-04 168672/week @ 2024-07-11 164665/week @ 2024-07-18 155291/week @ 2024-07-25 171851/week @ 2024-08-01 173135/week @ 2024-08-08 143851/week @ 2024-08-15

每月671,568次下载
用于 707 个crate (38直接)

Apache-2.0

44KB
377

github crates.io docs.rs

self_cell!

使用宏规则宏:self_cell!在稳定Rust中创建安全使用的自我引用结构体,而不会泄露结构体的内部生命周期。

总的来说,API看起来大致如下

// User code:

self_cell!(
    struct NewStructName {
        owner: Owner,

        #[covariant]
        dependent: Dependent,
    }
    
    impl {Debug}
);

// Generated by macro:

struct NewStructName(...);

impl NewStructName {
    fn new(
        owner: Owner,
        dependent_builder: impl for<'a> FnOnce(&'a Owner) -> Dependent<'a>
    ) -> NewStructName { ... }
    fn borrow_owner<'a>(&'a self) -> &'a Owner { ... }
    fn borrow_dependent<'a>(&'a self) -> &'a Dependent<'a> { ... }
    [...]
    // See the macro level documentation for a list of all generated functions
    // https://docs.rs/self_cell/latest/self_cell/macro.self_cell.html#generated-api.
}

impl Debug for NewStructName { ... }

目前,在安全Rust中不支持使用自我引用结构体。唯一合理的替代方案是要求用户处理两个独立的数据结构,这会很混乱。由于使用了进程宏,库解决方案ouroboros的编译成本非常高。

此替代方案是 no_std,不使用进程宏,一些自我包含的不安全代码,且在稳定Rust上运行,并通过miri测试。该crate包含不到300行实现代码,主要由类型和特性行为实现,旨在为自我引用结构体的问题提供一个良好的最小解决方案。

它已经经过了 经验丰富的Rust用户的社区代码审查

快速编译时间

$ rm -rf target && cargo +nightly build -Z timings

Compiling self_cell v0.9.0
Completed self_cell v0.9.0 in 0.2s

因为它不使用进程宏,且没有依赖项,所以编译时间很快。

在慢速笔记本电脑上进行的测量。

一个有说服力的用例

use self_cell::self_cell;

#[derive(Debug, Eq, PartialEq)]
struct Ast<'a>(pub Vec<&'a str>);

self_cell!(
    struct AstCell {
        owner: String,

        #[covariant]
        dependent: Ast,
    }

    impl {Debug, Eq, PartialEq}
);

fn build_ast_cell(code: &str) -> AstCell {
    // Create owning String on stack.
    let pre_processed_code = code.trim().to_string();

    // Move String into AstCell, then build Ast inplace.
    AstCell::new(
        pre_processed_code,
        |code| Ast(code.split(' ').filter(|word| word.len() > 1).collect())
    )
}

fn main() {
    let ast_cell = build_ast_cell("fox = cat + dog");

    println!("ast_cell -> {:?}", &ast_cell);
    println!("ast_cell.borrow_owner() -> {:?}", ast_cell.borrow_owner());
    println!("ast_cell.borrow_dependent().0[1] -> {:?}", ast_cell.borrow_dependent().0[1]);
}
$ cargo run

ast_cell -> AstCell { owner: "fox = cat + dog", dependent: Ast(["fox", "cat", "dog"]) }
ast_cell.borrow_owner() -> "fox = cat + dog"
ast_cell.borrow_dependent().0[1] -> "cat"

在安全Rust中无法有像 build_ast_cell 这样的API,因为一旦 Ast 依赖于像 pre_processed_code 这样的栈变量,你就不能再从函数中返回值了。你可以将预处理移动到调用者那里,但这会很快变得丑陋,因为你不能再封装东西了。注意,这是一个相当狭窄的用例,自我引用结构体仅应在没有更好的替代方案时使用。

在底层,它堆分配了一个结构体,首先通过将所有者值移动到它中来初始化它,然后使用对这个现在Pin/Immovable所有者的引用来构建依赖的结构体。这使得生成的SelfCell可以安全地移动,但你必须为堆分配付费。

有关更深入的API概述和高级示例,请参阅文档:https://docs.rs/self_cell

安装

查看cargo文档.

运行测试

cargo test

cargo miri test

最低要求的 rustc 版本

默认情况下,最低要求的 rustc 版本是 1.51。

有一个可选功能可以启用,称为 "old_rust",它支持 down 到 rustc 版本 1.36。然而,这需要使用技术上 UB 的版本来填充旧 rustc 的 std 库功能。测试没有显示旧 rustc 版本(ab)使用此功能。请自行承担风险。

最低版本是尽力而为的,并可能随着任何新的主要版本而更改。

贡献

在贡献时,请尊重 CODE_OF_CONDUCT.md

版本控制

我们使用 SemVer 进行版本控制。有关可用版本,请参阅 此存储库的标签

作者

有关参与此项目的 贡献者列表,也请参阅。

许可

本项目采用 Apache License,版本 2.0 - 请参阅 LICENSE.md 文件以获取详细信息。

依赖关系