4 ms·
The "trivial" equivalent to JavaScript is not RefCell, but plain old Cell, applied at the level of individual fields you want to write to. No panics and no (rel
by Rusky 4y ago
The "trivial" equivalent to JavaScript is not RefCell, but plain old Cell, applied at the level of individual fields you want to write to. No panics and no (relative) overhead there.
RefCell exists more as a bridge back to the world of &mut, since so much of the ecosystem uses that approach. But if you actually have a good reason to write JavaScript-like data structures (e.g. actual interop with a JavaScript-like language) then it just kind of gets in the way.
- verdagon 4y agoOf course, Cell has its own overhead, which is why using large Cells is often worse than just using Rc. It's also very situational; we often have types which don't work with Cell, and it often doesn't make sense to copy objects in many use cases. It's one of those things that works well in theory, but in practice can fall short.
- Rusky 4y agoThat's totally beside the point here: you can do a mechanical (thus "trivial") translation from JavaScript-like code to Rc+Cell, without ever hitting that overhead. That is, JavaScript-like code simply doesn't do large assignments in the first place. It doesn't directly hold move-only objects that are trickier to use with Cell. If you find yourself hitting those cases, you have left the realm of JavaScript-like code, and entered the realm of manual memory management.
- verdagon 4y ago> The limitations of &mut cannot be trivially worked around. > The "trivial" equivalent to JavaScript is not RefCell, but plain old Cell, applied at the level of individual fields you want to write to. No panics and no (relative) overhead there. I thought you were implying that we could trivially work around Rust's problems by just using Cell everywhere, my comment was in response to that. It seems now that you were talking about something else, so nevermind =)
- ridiculous_fish 4y agoI don't understand what you have in mind. Here's some basic JS, iterating over elements of a Node: for (let child of getElementById('parent').children) func(child) The equivalent in Rust is "risky" because func might also call getElementById('parent') and then that would panic. I think you are saying that Cell solves this by temporarily moving all children into a local, iterating over that, and then add them back as children? That seems pretty sketchy to me, you have these weird transient states where Nodes are temporarily removed from the DOM.
- Rusky 4y agoNo, I'm talking about matching JavaScript's memory layout and memory management, indirection and sharing and all. The children field is itself a reference to a collection. Reading the field creates a new reference to the same collection, and that is what the for loop holds. If func grabs parent that's fine, it's just one more reference (in the Rc sense). That collection also just holds references, and the for loop copies them out too. The trickiest bit here is the iteration state itself- you wouldn't be able to use e.g. Rust's typical slice iterator and still retain the ability to mutate the collection, but JavaScript doesn't do that either. Its iterators just have to be implemented taking the possibility of external mutation into account. The fundamental tradeoff here is that JavaScript simply puts everything behind a reference, all the time, while Rust allows you to work with objects directly. This is what makes sharing+mutation feel so simple in JavaScript- anything you can do to change the "shape" of a data structure is really only changing a reference, and leaving the old version around in case anyone else was still using it.
- ridiculous_fish 4y agoThank you, that clarifies the idea for me and it's an interesting approach. It seems like an "all in" design that you might apply to e.g. an interpreter. Of course in practice we don't abandon Rust's slice iterators to achieve basic observers or backreferences, and I've definitely tripped over "someone else is already borrowing this thing" runtime errors.