I would like to construct a parse tree of different node types that share functionality via a trait. I need different types because different kinds of nodes might have slightly different types of fields. For example, a branch node like a paragraph
does not hold text directly; only via its child Text
nodes does it have access to its concrete contents.
With this in mind, I've made the following specifications:
use std::cell::RefCell;
use std::rc::{Rc, Weak};
/// ### Parent
/// A shorthand for an optional (parent might not exist)
/// weak reference to a parent node.
type Parent<T> = Option<Weak<RefCell<T>>>;
/// ### Children
/// Shorthand for a vector of owned child nodes.
/// Empty vector indicates no children.
/// Leaf nodes will assert this.
type Children<T> = Vec<Rc<RefCell<T>>>;
/// ### Document
/// Document root node
pub struct Document<T: Node> {
id: usize,
id_counter: NodeId,
parent: Parent<T>,
children: Children<T>,
// -- snip --
}
/// ### trait Node
/// A trait defining functionality for general document tree nodes.
pub trait Node {
type ID;
type Parent;
type Children;
fn new() -> Self;
}
impl<T: Node> Node for Document<T> {
type ID = usize;
type Parent = Parent<T>;
type Children = Children<T>;
fn new() -> Self {
let mut id_counter = NodeId::new();
Document {
id: id_counter.assign(),
id_counter: id_counter,
parent: None,
children: Vec::new(),
// -- snip --
}
}
}
/// ### NodeId
/// A global counter of document nodes
#[derive(Debug)]
pub struct NodeId {
id: usize,
}
impl NodeId {
/// ### new
/// A NodeId constructor. In the beginning,
/// there are 0 Nodes.
pub fn new() -> Self {
NodeId { id: 0 }
}
/// ### increment
/// Increments the `NodeId` counter by 1.
pub fn increment(&mut self) {
self.id += 1;
}
/// ### get
/// Return a copy of the NodeId counter.NodeId
pub fn assign(&mut self) -> usize {
let current = self.id;
self.increment();
current
}
}
However, Rust will not let me construct a new empty document tree in a test. The code
#[test]
fn new_document_node() {
let doc = Document::new();
assert_eq!(0, doc.id);
}
results in the error
error[E0282]: type annotations needed for `Document<T>`
--> src/lib.rs:85:15
|
85 | let doc = Document::new();
| --- ^^^^^^^^^^^^^ cannot infer type for type parameter `T`
| |
| consider giving `doc` the explicit type `Document<T>`, where the type parameter `T` is specified
Adding a type annotation does not help:
let doc: Document<T: Node> = Document::new();
results in
error[E0658]: associated type bounds are unstable
--> src/lib.rs:85:23
|
85 | let doc: Document<T: Node> = Document::new();
| ^^^^^^^
|
= note: see issue #52662 <https://github.com/rust-lang/rust/issues/52662> for more information
^^^^^^^^^^^^^^^^^^^^^
error[E0107]: wrong number of type arguments: expected 1, found 0
--> src/lib.rs:85:14
|
85 | let doc: Document<T: Node> = Document::new();
| ^^^^^^^^^^^^^^^^^ expected 1 type argument
error[E0229]: associated type bindings are not allowed here
--> src/lib.rs:85:23
|
85 | let doc: Document<T: Node> = Document::new();
| ^^^^^^^ associated type not allowed here
Is it possible to create a tree of objects that implement common functionality, maybe slightly differently? For example, my leaf node types have no children, so their get_children() -> Option<Vec<Children>>
method would return an empty vector.
Is this at all achievable at least somewhat ergonomically in Rust?