Before we can start evolving anything, we have to figure out how to represent the individuals; we need to decide how to structure their chromosome. Many genetic algorithm implementations represent these as a [u8], serializing and deserializing as appropriate. There's a lot to recommend this representation. For one, modern computers have instructions specifically tailored to operate on many bytes in parallel—a topic we'll touch on in Chapter 10, Futurism – Near-Term Rust,—which is especially helpful in the mutation and reproduction stages of a genetic algorithm. You see, one of the key things to a successful simulation of evolution is speed. We need a large population and many, many generations to discover fit individuals. [u8] is a convenient representation for serialization and deserialization, especially with something such as serde (https://crates.io/crates/serde) and bincode (https://crates.io/crates/bincode) at hand. The downside to a [u8] representation is creating individuals that don't deserialize into something valid, in addition to eating up CPU time moving back and forth between [u8] and structs.
Program optimization comes down to carefully structuring one's computation to suit the computer or finding clever ways to avoid computation altogether. In this particular project, we do have a trick we can play. A valid individual is a finite list of instructions—feruscore targets producing load files—and all we have to do, then, is represent an instruction as a Rust struct and pop it into a vector. Not bad! This will save us a significant amount of CPU time, though our mutation step will be a tad slower. That's okay, though, as fitness evaluation will be the biggest time sink, as we'll see that shortly.