Is there a way to create this network without tons of cloning?

Once again I have found time in my personal life to learn rust, and I am resuming a pet project to do so. Thankfully each time I pick up rust it gets easier.

I want to take a genome (which tells me what the network connections should be) and create a network where nodes have references to neurons and neurons have references to nodes.

Here are all my types relevant to this question

pub struct Gene {
    pub in_node_id: usize,
    pub out_node_id: usize,
    pub weight: f64,
    enabled: bool
}

pub struct Genome(pub Vec<Gene>);

impl Genome {
    pub fn calculate_max_node_id(&self) -> usize {
        self.0.iter().fold(0, |acc, conn| {
            if conn.in_node_id > acc {
                conn.in_node_id
            } else if conn.out_node_id > acc {
                conn.out_node_id
            } else {
                acc
            }
        })
    }
}

enum NodeType{
    Sensor,
    Hidden,
    Output,
}

pub struct Node<'a> {
    value: f64,
    is_active: bool,
    has_active_inputs: bool,
    input_neurons: Vec<&'a Neuron<'a>>,
    active_sum: f64,
}

impl<'a> Node<'a> {
    fn create(node_type: NodeType) -> Node<'a> {
        Node{
            value: 0.,
            is_active: node_type == NodeType::Sensor,
            has_active_inputs: false,
            input_neurons: Vec::new(),
            active_sum: 0.,
        }
    }
}

pub struct Neuron<'a> {
    pub in_node: &'a Node<'a>,
    pub out_node: &'a Node<'a>,
    pub gene: &'a Gene,
}

pub struct Phenome<'a>(pub Vec<Neuron<'a>>);
pub struct Network<'a> {
    pub has_bias_node: bool,
    pub phenome: Phenome<'a>,
    pub nodes: Nodes<'a>,
    pub n_sensor_nodes: usize,
    pub n_output_nodes: usize,
}

Here is the bit of code I am struggling with

let init_nodes = Nodes::create_disconnected(n_sensor_nodes, n_output_nodes, max_node_id);
let (nodes, phenome) = genome.0.iter().fold((init_nodes, Phenome(Vec::new())), |(mut nodes, mut phenome), gene| {
    if gene.enabled {
        let in_node = &nodes.0[gene.in_node_id];
        let out_node = &nodes.0[gene.out_node_id];
        
        let neuron = Neuron{
            in_node,
            out_node,
            gene
        };
        nodes.0[gene.out_node_id].input_neurons.push(&neuron);
        phenome.0.push(neuron);
    }
    (nodes, phenome)
});

I hope my attempt clearly converys my intent. I tried to get this to work using various combinations of split_at_mut and split_first_mut but couldn't get it to work. I tried to use a fold because I usually think declaratively but happy to abandon it if there is a simpler way.

How exactly do you think this should work? You are taking the address to a local variable that is destroyed at the end of the block, and trying to store that reference in a larger scope. That's the definition of a use-after-free bug.

If you are building a data structure, you should store owned values, not references.

The struct Network has a vector of owned Nodes and Neurons. Each node references 1 or more Neurons and each Neuron references 2 nodes. I am trying to wire up all the references.

That's not what you are doing, though – you are literally taking the address of a local variable, not that of a vector element.

Anyway, it wouldn't work with the vector element, either. You are apparently trying to create a self-referential type, which is not possible in Rust. (Moving invalidates places, and every (sized) type is movable, so you can't store a reference to a value inside itself.)

Ah, interesting. I will chew on this fact for a bit. My previous design used index numbers as pointers. Maybe I will go back to that.

Yes, that's the usually recommended approach. Or you can use a dedicated graph library such as petgraph.

1 Like