Now that we understand structs and enums, we can learn about unions: a combination of the two concepts.
This is not the kind of union that $300k-earning Google employees fight for because they are "underpaid" and "don't have enough oat milk in the office kitchen". No, this feature is one that even Golang doesn't have (probably because they were worried about getting fired from Google for just mentioning the word!)
Unions in C can hold one of several types. They're like a less-strict sum type from the world of functional programming. Here's an example union:
typedef union AgeOrName {
int age;
char *name;
} age_or_name_t;
The age_or_name_t type can hold either an int or a char *, but not both at the same time (that would be a struct). We provide the list of possible types so that the C compiler knows the maximum potential memory requirement, and can account for that. This is how the union is used:
age_or_name_t lane = { .age = 29 };
printf("age: %d\n", lane.age);
// age: 29
Here's where it gets interesting. What happens if we try to access the name field (even though we set the age field)?
printf("name: %s\n", lane.name);
// name:
We get... nothing? To be more specific, we get undefined behavior. A union only reserves enough space to hold the largest type in the union and then all of the fields use the same memory. So when we set .age to 29, we are writing the integer representation of 29 to the memory of the lane union:
0000 0000 0000 0000 0000 0000 0001 1101
Then if we try to access .name, we read from the same block of memory but try to interpret the bytes as a char *, which is why we get garbage (which is interpreted as nothing in this case). Put simply, setting the value of .age overwrites the value of .name and vice versa, and you should only access the field that you set.
Sneklang is going to need objects. We'll hand-code those objects, and Sneklang developers will use them to store dynamic variables, kinda like Python. Everything is an object, even simple integers and strings!
Take a look at the SnekObject struct in exercise.h. It has a kind field that stores the type of the object (like INTEGER or STRING) and a data field that stores the actual data.
You can use sprintf to write the formatted string to the buffer. For example:
char buffer[100];
sprintf(buffer, "There are %d lights!", 4); // There are 4 lights!