The Const Keyword

For the longest time, I simply used the const keyword because it was a blanket command to use it. All tutorials and blog advised to use it. They all gave their explanations. I did not understand most of it. I just knew to use const when passing variables of certain types into a function - a const reference to be precise.

A few days ago while writing C++ for a little sqlite clone I was writing, it clicked! It just made sense.

I am asumming some familiarity with the idea of pointers and memory addresses and the reader is curious about how const relates to it.

In cpp if you pass a struct or objects into a function, you are basically passing a copy of it entirely to the function stack. You are passing by value. The function now has ownership of it. If that type is large, the stack could run out of memory. It is not as large as the heap.

1
2
3
4
5
6
7
struct GameState{
    float matrix[64]; // 256 bytes
    char name[128]; // 128 bytes
    int score[100]; //400 bytes
};                  // 784 bytes total

void process (GameState s) {} // 784 bytes passed in total to the stack!. Imagine this in hot loops environment or copying it over and over in various function calls.

Okay, good. The next question is: if I am not pass the entire value into the function stack, then how do I pass things? In cpp it is generally adviced, you pass by reference not value. The refernece is a “pointer” to a memory address. So instead of passing the entire value, we pass just the address of that varibale in memory. That is far cheaper - 8 bytes.

1
2
3
4
void process(GameState& s){} // Now you are passing just a reference to the GameState in memory (8 bytes) not the full structure of 784 bytes.

// But like every advice, there are instances where you abandon them. 
// int, float, bool are cheap. You do not generally need to pass them by reference.

The next problem is this: If I pass the reference which is just a memory address to the function, does that not mean that it can be mutated or modified by accident? is it not dangerous? is it not the very address holding the variable?

Well, that is where the beauty of const comes in. Const simply says in as much as you can see the variable in this memory address, you definitely cannot modifiy the value there. You can only read it. You cannot write to it. The notorious cpp compiler enforces this. Const is a promise stating that the value held in the memory address that was passed (&) will not mutated at all. The solves the problem.

If you want to modify it, sinply pass the reference to that address. If you do not want to modify it and just want to use it for other functions or read it, then use const.

1
2
3
4
5
6
7
void process(const GameState& s){
    // you get direct access to the memory address
    // no single copy is made
    // but the compiler is going to prevent any mutation on s.
    s.score = 99 ; // this will not be allowed.
    int newScore = s.score; // this is allowed. You are just reading it.
} // Nothing to clean up. You had a reference to it. You never owned it. Just borrowed.

Think that is the end ?

Absolutley not.

The next stage is this: A struct is declared. A function is declared in that struct. This function tries to mutate the value of the struct (which we clearly do not want modified). What will happen ?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct Server{
    int port;
    int connectionCount;

    // we declare a const function
    int getPort() const{
        port = 99; // this will return a compiler error as a const function is promising not to change the state of the varibale
        return port // this is allowed. Reading is fine. 
    }

    // we declare a non const function
    int increaseConnectionCount() {
        connectionCount++; // we can mutate it
    }

    void run(){
        port = 8080;
    }

    // This is what it implies: 
    void startServer(const Server &s) {
        s.run(); // This won't work. Why? Because the variable being passed is const but run isnt const. It is breaking the promise.

        s.getPort(); // This allowed. It is upholding the promise.
    }
}

Well the logic holds. Since the struct being passed into the function is not to be modified i.e. (const Server& s), then the function being called on it must also promise not to modify it. The function must be const. If you pass a const reference somewhere, every method called through it must also be a const. If a variable has been designed not to change, why should a function called through it change it?.

That my friends explains const. Its so simple and logical. The same concept holds in rust - only its the reverse. In rust immutability is the default while in cpp, you opt in for immutablility using the keyword const.