I'm trying to build a compute graph where some of the nodes in the graph need to store some internal state that they can modify on each render. Think caching, or an internal render counter. After trying a few approaches I discovered I could use RefCell for this.
To simplify things my compute node called Op takes a single input and returns a single output:
My question is, is this an idiomatic way of using Rust or is there a better way that would avoid the runtime cost of RefCells? I've tried making render take a mutable reference but got in a fight with the borrow checker.
At least from what you posted, I don't see a reason you couldn't use normal mutable borrows. The determining factor is where you get your reference to the Op from. In your example you have a vector of owned Ops, so you can just borrow any of them and mutate them as you like. RefCells are necessary when you have shared ownership.
I would prefer getting this working with normal mutable borrows. The issue I got stuck with is that I'm borrowing things more than once in the Network::render method. Rendering a single node works fine, but I couldn't get it working when doing it recursively.
Ideally the render method would look like this (but Rust can't make the guarantees it needs in this form):
fn render(&mut self, op_name: &str) -> i32 {
// Find the op to render
let mut op = self.find_op_mut(&op_name);
// Provide a default value for its input
let mut input = 42;
// Render its input (recursive)
for connection in &self.connections {
if connection.input == op.name() {
let mut out_op = self.find_op_mut(&connection.output);
input = self.render(out_op.name());
break;
}
}
// Render the op itself
op.render(input)
}
Thanks! Working with indices instead of references is a great idea. I'll probably take this a bit further and see if I can make a mapping between internal ID's and Ops; this would make lookup faster as well.
Can't claim credit for the idea - that's pretty much the canonical way to work around aliasing and borrowing issues in non-linear borrowing scenarios.
The downside, of course, is the indices are subject to staleness - i.e. if a modification is made somewhere, but the index isn't updated, you'll have a bug that may be tricky to track down. References prevent this statically but of course bring their own caveats to the table.