10个版本

使用旧的Rust 2015

0.1.1 2016年9月2日
0.0.9 2016年9月2日
0.0.7 2016年6月6日
0.0.6 2016年2月17日
0.0.1 2015年6月2日

#353 in 内存管理

26 每月下载量

MPL-2.0 许可证

5KB
94 代码行

rust-gc

Build Status

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

设计和动机在这篇博客文章中展示,代码草稿在这个gist中。

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

如何使用

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

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

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

虽然这可以广泛使用,但它旨在仅在需要时使用,遵循Rust的“按需付费”模式。避免在RcBox同样可用时使用Gc

放在Gc内部的类型必须实现TraceFinalize。最简单的方法是使用gc_derive crate

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方法来使事情在同时运行)。

注意,Trace仅适用于 transitively 包含 Gc 的类型,如果你确定不是这种情况,你可以在你的类型上使用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 类似,要获得可变性,我们必须使用单元格类型。标准库中的常规 RefCell 无法与 Gc 一起使用(因为它没有实现 Trace),而是使用 GcCellGcCell 的行为与 RefCell 非常相似,不同之处在于它内部帮助跟踪 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 自定义 derive 自动实现 Drop 并使用安全的空 drop 方法来强制执行。应使用 Finalize 进行清理。
  • 需要有一个更好的跨 crate 继承方案。
  • 当前的 GC 不是并发的,GCed 对象被限制在单个线程中。有一个实验性的并发收集器 在这个 pull request 中
  • RuScript:使用单线程 rust-gc 为各种对象分配内存

没有运行时依赖