#垃圾 #内存 #垃圾回收 #

gc_derive

rust-gc的垃圾收集器 derive 插件

11 个版本

0.5.0 2023年7月29日
0.4.1 2021年5月10日
0.4.0 2021年2月16日
0.3.6 2020年6月9日
0.2.1 2016年11月18日

#38#垃圾

Download history 518/week @ 2024-03-13 502/week @ 2024-03-20 823/week @ 2024-03-27 443/week @ 2024-04-03 394/week @ 2024-04-10 381/week @ 2024-04-17 438/week @ 2024-04-24 344/week @ 2024-05-01 320/week @ 2024-05-08 442/week @ 2024-05-15 345/week @ 2024-05-22 371/week @ 2024-05-29 263/week @ 2024-06-05 220/week @ 2024-06-12 255/week @ 2024-06-19 224/week @ 2024-06-26

1,033 每月下载量
19 包中使用 (6 直接使用)

MPL-2.0 许可证

9KB
70

rust-gc

Build Status

Rust的简单跟踪(标记和清除)垃圾收集器

设计和动机在这篇博客文章中说明,代码草稿见此gist

还有一篇关于cgc初始设计的文章,其实验性并发分支。

如何使用

要将它包含到你的项目中,在Cargo.toml中添加以下内容

[dependencies]
gc = { version = "0.4", features = ["derive"] }

它可以像Rc一样使用,除了内部可变性。

尽管它可以广泛使用,但它的目的是仅在需要时使用,遵循Rust的“按需付费”模型。在可以使用RcBox的情况下避免使用Gc

放入Gc中的类型必须实现TraceFinalize。最简单的方法是使用gc_derive

use gc::{Finalize, Gc, Trace};

#[derive(Trace, Finalize)]
struct Foo {
    x: Option<Gc<Foo>>,
    y: u8,
    // ...
}

// now, `Gc<Foo>` may be used

Finalize也可以直接在结构体上实现,以添加自定义的清理行为

use gc::{Finalize, Trace};

#[derive(Trace)]
struct Foo {...}

impl Finalize for Foo {
    fn finalize(&self) {
        // Clean up resources for Foo, because we think it will be destroyed.
        // Foo may not be destroyed after a call to finalize, as another
        // finalizer may create a reference to it due to reference cycles.
    }
}

对于在stdlib中定义的类型,请在此存储库中提交一个问题(使用下面显示的unsafe_ignore_trace方法在同时使事情运行)。

请注意,只有当类型递归包含Gc时,才需要Trace,如果您确定不是这种情况,可以在您的类型上使用unsafe_empty_trace!宏。或者,在结构体字段上使用#[unsafe_ignore_trace]注解。不正确使用unsafe_empty_traceunsafe_ignore_trace可能导致不安全。

use gc::{Finalize, Gc, Trace};
use bar::Baz;

#[derive(Trace, Finalize)]
struct Foo {
    x: Option<Gc<Foo>>,
    #[unsafe_ignore_trace]
    y: Baz, // we are assuming that `Baz` doesn't contain any `Gc` objects
    // ...
}

要使用Gc,只需调用Gc::new

let x = Gc::new(1_u8);
let y = Gc::new(Box::new(Gc::new(1_u8)));

#[derive(Trace, Finalize)]
struct Foo {
    a: Gc<u8>,
    b: u8
}

let z = Gc::new(Foo {a: x.clone(), b: 1})

Gc上调用clone()将创建对同一对象的另一个垃圾收集引用。在可能的情况下,尽量使用内部值的借用引用而不是克隆Gc,因为Gc实现了Deref并与借用兼容。

Gc是一个不可变容器。就像使用Rc一样,要获得可变性,我们必须使用单元格类型。stdlib中的常规RefCell不能与Gc一起使用(因为它没有实现Trace),而是使用GcCell。与RefCell类似,GcCell的行为非常相似,只是它内部帮助跟踪GC根。

#[derive(Trace, Finalize)]
struct Foo {
    cyclic: GcCell<Option<Gc<Foo>>>,
    data: u8,
}

let foo1 = Gc::new(Foo {cyclic: GcCell::new(None), data: 1});
let foo2 = Gc::new(Foo {cyclic: GcCell::new(Some(foo1.clone())), data: 2});
let foo3 = Gc::new(Foo {cyclic: GcCell::new(Some(foo2.clone())), data: 3});
*foo1.cyclic.borrow_mut() = Some(foo3.clone());

已知问题

  • 析构函数不应访问Gc/GcCell值。这通过Trace自定义派生自动实现Drop并使用安全的空释放方法来强制执行。Finalize应用于清理。
  • 跨crate派生的故事需要更好。
  • 当前的GC不是并发的,并且GC对象限制在单个线程中。在这个拉取请求中有一个实验性的并发收集器。
  • RuScript:使用单线程rust-gc为各种对象分配内存

依赖关系

~1.5MB
~38K SLoC