Rust 中的"类似 DOM"树数据结构的各种实现策略。
This repository exists to publish the results of some experiments that I did over a couple week-ends that one time. This is not a software project that is being maintained over time.
If you would like to use this code, consider copying into your own source repository under the terms of the MIT license.
Various implementation strategies for “DOM-like” tree data structures in Rust.
“DOM-like” here means that data structures can be used to represent the parsed content of an HTML or XML document, like the DOM does, but don’t necessarily have the exact same API as the DOM. That is:
In particular, the need to access parents means we can not use the obvious structure of child nodes being owned directly by their parent:
struct Node {
data: T,
children: Vec>,
parent: /* ??? */
}
More generally, a tree with parent and sibling relationships (in addition to children relationships) can be viewed as a graph of a special kind, but still a graph that contains cycles. And Rust’s default ownership model does not support cycles easily. Therefore, we need a more involved strategy to manage the lifetime of nodes.
In Servo, DOM nodes are managed by the JavaScript garbage collector. Rust however currently does not have a tracing garbage collector of its own, and many Rust projects might want to manipulate DOM-like trees without embedding an entire JavaScript virtual machine.
This repository contains multiple crates that each implement a DOM-like data structure with a different approach and different trade-offs.
rctreeThe lifetime of nodes is managed through reference counting. To avoid reference cycles which would cause memory leaks, the tree is asymmetric: each node holds optional strong references to its next sibling and first child, but only optional weak references to its parent, previous sibling, and last child.
Nodes are destroyed as soon as there is no strong reference left to them. The structure is such that holding a reference to the root is sufficient to keep the entire tree alive. However, if for example the only reference that exists from outside the tree is one that you use to traverse it, you will not be able to go back “up” the tree to ancestors and previous siblings after going “down”, as those nodes will have been destroyed.
Weak references to destroyed nodes are treated as if they were not set at all. (E.g. a node can become a root when its parent is destroyed.)
Since nodes are aliased (have multiple references to them),
RefCell is used for interior mutability.
Advantages:
NodeRef user-visible type to manipulate the tree, with methodsDisadvantages:
arena-treeThe lifetime of nodes is managed through an arena allocator.
Nodes are tied, through &'a T references, to the lifetime of the arena
and are destroyed all at once when the arena is destroyed.
The links between nodes are also &'a T references internally.
Since nodes are aliased (have multiple references to them),
Cell is used for interior mutability.
Advantages:
Disadvantages:
idtreeSimilar to arena-tree, but the arena is simplified to a single Vec
and numerical identifiers (indices in the vector) are used instead of &'a T references.
Advantages:
RefCell, mutability is handled in a way much more idiomatic to Rust
through unique (&mut) access to the arena.Vec.
This enables e.g. parallel tree traversals.Disadvantages:
arena-tree because of bound checks.暂无开放 Issues,或尚未同步最近议题。