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 在 #垃圾 中
1,033 每月下载量
在 19 个 包中使用 (6 直接使用)
9KB
70 行
rust-gc
Rust的简单跟踪(标记和清除)垃圾收集器
还有一篇关于cgc
初始设计的文章,其实验性并发分支。
如何使用
要将它包含到你的项目中,在Cargo.toml中添加以下内容
[dependencies]
gc = { version = "0.4", features = ["derive"] }
它可以像Rc
一样使用,除了内部可变性。
尽管它可以广泛使用,但它的目的是仅在需要时使用,遵循Rust的“按需付费”模型。在可以使用Rc
或Box
的情况下避免使用Gc
。
放入Gc
中的类型必须实现Trace
和Finalize
。最简单的方法是使用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_trace
和unsafe_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