How do I avoid double borrow in this loop in a loop?

Why do you need the outer loop? In the example you show, you only need to iterate the connections vector, look up and modify the downsteam nodes.

If you have more operations on the downstream node, you can iterate over them after the first loop.

impl Network2 {
    fn activation_pulse(mut self) -> Network2 {
		for conn in self.genome.iter() {
			let in_node = &self.nodes[conn.in_node_id]; // here (conn.in_node_id)
			if in_node.is_active && conn.enabled {
				let to_add = conn.weight * in_node.value;
				let out_node = &mut self.nodes[node_ix]; // here (node_ix)
				out_node.has_active_inputs = true;
				out_node.active_sum += to_add;

Or, if it has to be activated after inputs (if you model electric network), then maybe its more reasonable to iterate over the network like in Dijkstra algorithm (or breadth-first search), keeping a queue (simple vector) of nodes to process. Except in your case it's better to keep outgoing connections ids rather than incoming:

struct Node2{
    value: f64,
    is_active: bool,
    has_active_inputs: bool,
    output_connections: Vec<usize>,
    active_sum: f64,