Skip to content

Instantly share code, notes, and snippets.

@jdm
Last active May 3, 2021 13:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jdm/9900569 to your computer and use it in GitHub Desktop.
Save jdm/9900569 to your computer and use it in GitHub Desktop.
struct Node {
shared_ptr<Node> parent;
shared_ptr<Node> first_child;
TextNode* as_text_node() { assert(/*sensible check*/); return (TextNode*)this; }
virtual Element* as_element() { return NULL; }
};
struct TextNode: public Node {
};
struct Element: public Node {
vector<string, string> attrs;
void set_attribute(const string& key, const string& value);
virtual void before_set_attr(const string& key, const string& value);
virtual void after_set_attr(const string& key, const string& value);
virtual Element* as_element() { return this; }
};
void Element::set_attribute(const string& key, const string& value)
{
before_set_attr(key, value);
//...update attrs...
after_set_attr(key, value);
}
struct HTMLImageElement: public Element {
virtual void before_set_attr(const string& key, const string& value);
};
void HTMLImageElement::before_set_attr(const string& key, const string& value)
{
if (key == "src") {
//..remove cached image with url |value|...
}
Element::before_set_attr(key, value);
}
struct HTMLVideoElement: public Element {
bool cross_origin;
void after_set_attr(const string& key, const string& value);
};
void HTMLVideoElement::after_set_attr(const string& key, const string& value)
{
if (key == "crossOrigin") {
cross_origin = value == "true";
}
Element::after_set_attr(key, value);
}
void process_any_element(Element* element) {
//...
}
shared_ptr<HTMLVideoElement> videoElement = ...;
process_any_element(videoElement);
shared_ptr<Node> node = videoElement->first_child;
shared_ptr<Element> element = node->as_element();
if (!element) {
shared_ptr<TextNode> text = node->as_text_node();
}
@jdm
Copy link
Author

jdm commented Mar 31, 2014

One added concern with representing the DOM in Servo: the vast majority of our DOM methods take &JS<T> smart pointer arguments instead of &T, where T can be a type in the Node hierarchy. It would be swell if we could pass &JS<Element> to a method taking &JS<Node>.

@nrc
Copy link

nrc commented Mar 31, 2014

The & vs JS pointer thing should mostly just work (TM) with any system, it is a goal of Rust to support smart pointer use like that and its a goal of our work on DST and variance. I guess the only thing to be aware of is if we only do virtual dispatch on pointer-to-T, then this should work for smart pointers to.

@MicahChalmer
Copy link

While trying to translate this into rust+rust-lang/rfcs/#9, I noticed an error in this part:

shared_ptr<Node> node = videoElement->first_child;
shared_ptr<Element> element = node->as_element();
if (!element) {
  shared_ptr<TextNode> text = node->as_text_node();
}

If node is an Element, then the local variable element is created as a new shared_ptr that thinks it has sole ownership of it--it is not sharing with node. So element and node will both try to free the same object when they go out of scope, and if one outlive the other you could access an already-destroyed object through the longer-lived one. Same problem for text.

This would still compile fine in C++, of course, which illustrates why we all want Rust. The proposals for modeling the DOM in rust may look different, but none of them would let something like that compile.

I think this is what you want:

shared_ptr<Node> node = videoElement->first_child;
Element* element = node->as_element();
if (!element) {
  TextNode* text = node->as_text_node();
}

where element and text would be borrowed pointers in translated rust versions. Do I have this right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment